diff --git a/bril-ts/builder.ts b/bril-ts/builder.ts index 775948c08..0f68f9924 100644 --- a/bril-ts/builder.ts +++ b/bril-ts/builder.ts @@ -67,7 +67,7 @@ export class Builder { return this.buildEffect("call", args, [func], undefined); } } - + /** * Build a constant instruction. As above, the destination name is optional. */ @@ -124,6 +124,32 @@ export class Builder { this.curFunction.instrs.push(instr); } + /** + * Checks whether the last emitted instruction in the current function is the specified op code. + * Useful for checking for terminating instructions. + */ + isLastEmittedEffectOp(op: bril.EffectOpCode): boolean { + if (!this.curFunction) { + return false + } + + if (!this.curFunction.instrs) { + return false + } + + if (this.curFunction.instrs.length === 0) { + return false + } + + const last_instr : bril.Instruction | bril.Label = this.curFunction.instrs[this.curFunction.instrs.length - 1]; + + if ('label' in last_instr) { + return false + } + + return last_instr.op === op + } + /** * Generate an unused variable name. */ diff --git a/test/ts/factorial.out b/test/ts/factorial.out index cf0ff0cdb..b2cd75b60 100644 --- a/test/ts/factorial.out +++ b/test/ts/factorial.out @@ -16,9 +16,7 @@ .then.0: v4: int = const 1; ret v4; - jmp .endif.0; .else.0: -.endif.0: v5: int = id x; v6: int = id x; v7: int = const 1; diff --git a/test/ts/simplified.out b/test/ts/simplified.out new file mode 100644 index 000000000..cf4144983 --- /dev/null +++ b/test/ts/simplified.out @@ -0,0 +1,13 @@ +@main { + v0: int = const 1; + call @erronious v0; + v1: int = const 0; +} +@erronious(x: int): int { + v1: bool = const true; + br v1 .then.0 .else.0; +.then.0: + v2: int = id x; + ret v2; +.else.0: +} diff --git a/test/ts/simplified.ts b/test/ts/simplified.ts new file mode 100644 index 000000000..6e53b939b --- /dev/null +++ b/test/ts/simplified.ts @@ -0,0 +1,8 @@ +erronious(1n); + +// This snippet checks that the ts compiler doesn't try to create an unreachable else branch +function erronious(x: bigint): bigint { + if (true) { + return x; + } +} diff --git a/ts2bril.ts b/ts2bril.ts index f2a1929ac..7b198015b 100644 --- a/ts2bril.ts +++ b/ts2bril.ts @@ -145,7 +145,7 @@ function emitBril(prog: ts.Node, checker: ts.TypeChecker): bril.Program { // Check if effect statement, i.e., a call that is not a subexpression if (call.parent.kind === ts.SyntaxKind.ExpressionStatement) { - builder.buildCall(callText, + builder.buildCall(callText, values.map(v => v.dest)); return builder.buildInt(0); // Expressions must produce values } else { @@ -153,12 +153,12 @@ function emitBril(prog: ts.Node, checker: ts.TypeChecker): bril.Program { let type = brilType(decl, checker); let name = (decl.name != undefined) ? decl.name.getText() : undefined; return builder.buildCall( - callText, - values.map(v => v.dest), - type, + callText, + values.map(v => v.dest), + type, name, ); - } + } } default: throw `unsupported expression kind: ${expr.getText()}`; @@ -215,12 +215,18 @@ function emitBril(prog: ts.Node, checker: ts.TypeChecker): bril.Program { // Statement chunks. builder.buildLabel(thenLab); emit(if_.thenStatement); - builder.buildEffect("jmp", [], undefined, [endLab]); + const then_branch_terminated = builder.isLastEmittedEffectOp("ret"); + if (!then_branch_terminated) { + builder.buildEffect("jmp", [], undefined, [endLab]); + } builder.buildLabel(elseLab); if (if_.elseStatement) { emit(if_.elseStatement); } - builder.buildLabel(endLab); + // The else branch otherwise just falls through without needing a target label + if (!then_branch_terminated) { + builder.buildLabel(endLab); + } break; } @@ -258,7 +264,7 @@ function emitBril(prog: ts.Node, checker: ts.TypeChecker): bril.Program { break; } - case ts.SyntaxKind.FunctionDeclaration: + case ts.SyntaxKind.FunctionDeclaration: let funcDef = node as ts.FunctionDeclaration; if (funcDef.name === undefined) { throw `no anonymous functions!`; @@ -298,7 +304,7 @@ function emitBril(prog: ts.Node, checker: ts.TypeChecker): bril.Program { case ts.SyntaxKind.ImportDeclaration: break; - + default: throw `unhandled TypeScript AST node kind ${ts.SyntaxKind[node.kind]}`; }