diff --git a/ast/ast.ts b/ast/ast.ts index aaed5b78..4951925f 100644 --- a/ast/ast.ts +++ b/ast/ast.ts @@ -1816,6 +1816,7 @@ module TDev.AST { public _isActionTypeDef:boolean; public _compilerInlineAction:InlineAction; public _compilerParentAction:Action; // this is only set for synthetic actions created in compiler for lambda expressions + public _compilerInlineBody:Stmt; public _skipIntelliProfile:boolean; public allLocals:LocalDef[]; public accept(v:NodeVisitor) { return v.visitAction(this); } @@ -1855,6 +1856,13 @@ module TDev.AST { return res } + public getShimName():string + { + var shm = /{shim:([^{}]*)}/.exec(this.getDescription()) + if (shm) return shm[1] + return null + } + public getFlags() { var flags = !this.isAtomic ? PropertyFlags.Async : PropertyFlags.None diff --git a/ast/typeChecker.ts b/ast/typeChecker.ts index 720bb71e..4c2a97d9 100644 --- a/ast/typeChecker.ts +++ b/ast/typeChecker.ts @@ -2,7 +2,7 @@ // TODO events and async -// Next available error: TD212: +// Next available error: TD214: module TDev.AST { @@ -96,7 +96,7 @@ module TDev.AST private isTopExpr = false; static lastStoreLocalsAt:ExprHolder; - static lintThumb : (asm:string, err:(msg:string) => void) => void; + static lintThumb : (a:Action, asm:string, err:(msg:string) => void) => void; constructor() { super() @@ -864,7 +864,7 @@ module TDev.AST node.clearError(); this.actionSection = ActionSection.Normal; this.inAtomic = node.isAtomic; - this.inShim = this.topApp.entireShim || /{shim:.*}/.test(node.getDescription()) + this.inShim = this.topApp.entireShim || node.getShimName() != null; this.scope(() => { // TODO in - read-only? @@ -1778,8 +1778,11 @@ module TDev.AST break; case "thumb": if (!checkArgumentCount(2)) return; + if (!this.inShim) + this.markError(t, lf("TD213: app->thumb only supported inside of {shim:}")) + this.currentAction.getOutParameters().forEach(p => this.recordLocalWrite(p.local)) if (TypeChecker.lintThumb) - TypeChecker.lintThumb(t.args[1].getStringLiteral(), e => this.markError(t, e)) + TypeChecker.lintThumb(this.currentAction, t.args[1].getStringLiteral(), e => this.markError(t, e)) break; case "import": if (!checkArgumentCount(4)) return; diff --git a/embedded/bytecode.ts b/embedded/bytecode.ts index 3cc90a5c..048f7f44 100644 --- a/embedded/bytecode.ts +++ b/embedded/bytecode.ts @@ -745,8 +745,16 @@ module TDev.AST.Bytecode { useAction(a:Action) { - if (/{shim:.*}/.test(a.getDescription())) - return + if (a.getShimName() != null && !a._compilerInlineBody) { + a.body.stmts.forEach(s => { + var str = AST.getEmbeddedLangaugeToken(s) + if (str && (s).expr.parsed.getCalledProperty().getName() == "thumb") { + a._compilerInlineBody = s; + } + }) + if (!a._compilerInlineBody) + return + } super.useAction(a) } } @@ -948,6 +956,11 @@ module TDev.AST.Bytecode this.proc.emit("push {r1}"); } + reportError(msg:string) + { + Util.userError(msg) + } + handleActionCall(e:Call) { var aa = e.calledExtensionAction() || e.calledAction() @@ -962,15 +975,15 @@ module TDev.AST.Bytecode Util.assert(args.length == aa.getInParameters().length) var a:Action = aa._compilerInfo || aa - var shm = /{shim:([^{}]*)}/.exec(a.getDescription()) + var shm = a.getShimName(); var hasret = !!aa.getOutParameters()[0] - if (shm && shm[1] == "TD_NOOP") { + if (shm == "TD_NOOP") { Util.assert(!hasret) return } - if (shm && /^micro_bit::(createImage|showAnimation|showLeds)$/.test(shm[1])) { + if (/^micro_bit::(createImage|showAnimation|showLeds)$/.test(shm)) { Util.assert(args[0].getLiteral() != null) this.emitImageLiteral(args[0].getLiteral()) args.shift() @@ -982,25 +995,41 @@ module TDev.AST.Bytecode } - if (shm) { + if (shm != null) { var mask = this.getMask(args) - var msg = "{shim:" + shm[1] + "} from " + a.getName() - if (!shm[1]) - Util.oops("called " + msg) + var msg = "{shim:" + shm + "} from " + a.getName() + if (!shm) + Util.oops("called " + msg + " (with empty {shim:}") - var inf = lookupFunc(shm[1]) + var inf = lookupFunc(shm) - if (!inf) - Util.oops("no such " + msg) - - if (!hasret) { - Util.assert(inf.type == "P", "expecting procedure for " + msg); + if (a._compilerInlineBody) { + if (inf) + this.reportError(lf("app->thumb inline body not allowed in {shim:{0}} (already defined in runtime)", shm)) + funcInfo[shm] = { + type: hasret ? "F" : "P", + args: a.getInParameters().length, + idx: 0, + value: 0 + } + try { + this.proc.emitCall(shm, mask) + } finally { + delete funcInfo[shm] + } } else { - Util.assert(inf.type == "F", "expecting function for " + msg); - } - Util.assert(args.length == inf.args, "argument number mismatch: " + args.length + " vs " + inf.args + " in " + msg) + if (!inf) + Util.oops("no such " + msg) - this.proc.emitCall(shm[1], mask) + if (!hasret) { + Util.assert(inf.type == "P", "expecting procedure for " + msg); + } else { + Util.assert(inf.type == "F", "expecting function for " + msg); + } + Util.assert(args.length == inf.args, "argument number mismatch: " + args.length + " vs " + inf.args + " in " + msg) + + this.proc.emitCall(shm, mask) + } } else { this.proc.emit("bl " + this.procIndex(a).label) if (args.length > 0) { @@ -1395,6 +1424,15 @@ module TDev.AST.Bytecode this.proc = this.procIndex(a); + if (a.getShimName() != null) { + var body = AST.getEmbeddedLangaugeToken(a._compilerInlineBody) + Util.assert(body != null) + Util.assert(body.getStringLiteral() != null) + this.proc.emit(body.getStringLiteral()) + this.proc.emit("@stackempty func"); + return + } + var ret = this.proc.mkLabel("actret") a._compilerBreakLabel = ret; this.dispatch(a.body) @@ -1461,6 +1499,25 @@ module TDev.AST.Bytecode p.action = a; this.binary.addProc(p) + var shimname = a.getShimName() + if (shimname != null) { + Util.assert(!!a._compilerInlineBody) + + if (!/^\w+$/.test(shimname)) + this.reportError(lf("invalid inline shim name: {shim:{0}}", shimname)) + + if (a.getInParameters().length > 4) + this.reportError(lf("inline shims support only up to 4 arguments")); + + this.proc.label = shimname + + this.proc.emit(".section code"); + this.proc.emitLbl(this.proc.label); + this.proc.emit("@stackmark func"); + return + } + + var inparms = a.getInParameters().map(p => p.local) this.proc.args = inparms.map((p, i) => { var l = new Location(i, p); @@ -1477,6 +1534,7 @@ module TDev.AST.Bytecode this.proc.pushLocals(); visitStmts(a.body, s => { + if (s instanceof ExprStmt) { var ai = (s).expr.assignmentInfo() if (ai) { @@ -1547,13 +1605,13 @@ module TDev.AST.Bytecode } } - function lintThumb(asm:string, err:(msg:string) => void) + function lintThumb(act:Action, asm:string, err:(msg:string) => void) { setup(); var code = ".section code\n" + "@stackmark base\n" + - "_start:\n" + + act.getShimName() + ":\n" + asm + "\n" + "@stackempty base\n" var b = new Thumb.Binary(); @@ -1563,14 +1621,15 @@ module TDev.AST.Bytecode if (b.errors.length > 0) { b.errors.forEach(e => console.log(e.message)) - err(b.errors.map(e => e.message).join("\n")) + err(lf("TD212: thumb assembler error") + "\n" + + b.errors.map(e => e.message).join("\n")) } } TypeChecker.lintThumb = lintThumb; function asmline(s:string) { - if (!/:$/.test(s)) + if (!/(^\s)|(:$)/.test(s)) s = " " + s return s + "\n" }