Implement ternary using if, see AssemblyScript/assemblyscript#123

This commit is contained in:
dcodeIO 2017-12-04 14:49:24 +01:00
parent 558a4d5c63
commit c6af2d1454
11 changed files with 245 additions and 53 deletions

2
assembly.d.ts vendored
View File

@ -66,7 +66,7 @@ declare function current_memory(): i32;
/** Grows linear memory by a given unsigned delta of pages. One page is 64kb. Returns the previous memory size in units of pages or `-1` on failure. */ /** Grows linear memory by a given unsigned delta of pages. One page is 64kb. Returns the previous memory size in units of pages or `-1` on failure. */
declare function grow_memory(value: i32): i32; declare function grow_memory(value: i32): i32;
/** Emits an unreachable operation that results in a runtime error when executed. */ /** Emits an unreachable operation that results in a runtime error when executed. */
declare function unreachable(): void; declare function unreachable(): any; // sic
/** Loads a value of the specified type from memory. */ /** Loads a value of the specified type from memory. */
declare function load<T>(offset: usize): T; declare function load<T>(offset: usize): T;

View File

@ -8,5 +8,14 @@
"desc": "Print this message.", "desc": "Print this message.",
"type": "boolean", "type": "boolean",
"aliases": [ "h" ] "aliases": [ "h" ]
},
"optimize": {
"desc": "Optimize the module.",
"type": "boolean",
"aliases": [ "O" ]
},
"validate": {
"desc": "Validate the module.",
"type": "boolean"
} }
} }

View File

@ -8,6 +8,7 @@ import * as as from "../src";
var conf: { [key: string]: { desc: string, type: string, aliases: string[], default: any } } = require("./asc.json"); var conf: { [key: string]: { desc: string, type: string, aliases: string[], default: any } } = require("./asc.json");
var opts: minimist.Opts = {}; var opts: minimist.Opts = {};
Object.keys(conf).forEach(key => { Object.keys(conf).forEach(key => {
var opt = conf[key]; var opt = conf[key];
if (opt.aliases) if (opt.aliases)
@ -80,7 +81,7 @@ let diagnostic: as.DiagnosticMessage | null;
let hasErrors: boolean = false; let hasErrors: boolean = false;
while ((diagnostic = as.nextDiagnostic(parser)) != null) { while ((diagnostic = as.nextDiagnostic(parser)) != null) {
console.error(as.formatDiagnostic(diagnostic, process.stdout.isTTY, true)); console.error(as.formatDiagnostic(diagnostic, process.stderr.isTTY, true));
if (as.isError(diagnostic)) if (as.isError(diagnostic))
hasErrors = true; hasErrors = true;
} }
@ -89,5 +90,27 @@ if (hasErrors)
process.exit(1); process.exit(1);
const module = as.compile(parser); const module = as.compile(parser);
hasErrors = false;
while ((diagnostic = as.nextDiagnostic(parser)) != null) {
console.error(as.formatDiagnostic(diagnostic, process.stderr.isTTY, true));
if (as.isError(diagnostic))
hasErrors = true;
}
if (hasErrors) {
module.dispose();
process.exit(1);
}
if (args["validate"])
if (!module.validate()) {
module.dispose();
process.exit(1);
}
if (args["optimize"])
module.optimize();
_BinaryenModulePrint(module.ref); _BinaryenModulePrint(module.ref);
module.dispose(); module.dispose();

View File

@ -19,7 +19,7 @@
ParenthesizedExpression ParenthesizedExpression
NewExpression NewExpression
PropertyAccessExpression PropertyAccessExpression
SelectExpression TernaryExpression
UnaryExpression UnaryExpression
UnaryPostfixExpression UnaryPostfixExpression
UnaryPrefixExpression UnaryPrefixExpression
@ -101,7 +101,7 @@ export enum NodeKind {
NULL, NULL,
PARENTHESIZED, PARENTHESIZED,
PROPERTYACCESS, PROPERTYACCESS,
SELECT, TERNARY,
SUPER, SUPER,
THIS, THIS,
TRUE, TRUE,
@ -302,8 +302,8 @@ export abstract class Expression extends Node {
return expr; return expr;
} }
static createSelect(condition: Expression, ifThen: Expression, ifElse: Expression, range: Range): SelectExpression { static createTernary(condition: Expression, ifThen: Expression, ifElse: Expression, range: Range): TernaryExpression {
const expr: SelectExpression = new SelectExpression(); const expr: TernaryExpression = new TernaryExpression();
expr.range = range; expr.range = range;
(expr.condition = condition).parent = expr; (expr.condition = condition).parent = expr;
(expr.ifThen = ifThen).parent = expr; (expr.ifThen = ifThen).parent = expr;
@ -549,9 +549,9 @@ export class RegexpLiteralExpression extends LiteralExpression {
} }
} }
export class SelectExpression extends Expression { export class TernaryExpression extends Expression {
kind = NodeKind.SELECT; kind = NodeKind.TERNARY;
condition: Expression; condition: Expression;
ifThen: Expression; ifThen: Expression;
ifElse: Expression; ifElse: Expression;

View File

@ -59,7 +59,7 @@ import {
NewExpression, NewExpression,
ParenthesizedExpression, ParenthesizedExpression,
PropertyAccessExpression, PropertyAccessExpression,
SelectExpression, TernaryExpression,
StringLiteralExpression, StringLiteralExpression,
UnaryPostfixExpression, UnaryPostfixExpression,
UnaryPrefixExpression, UnaryPrefixExpression,
@ -927,8 +927,8 @@ export class Compiler extends DiagnosticEmitter {
expr = this.compilePropertyAccessExpression(<PropertyAccessExpression>expression, contextualType); expr = this.compilePropertyAccessExpression(<PropertyAccessExpression>expression, contextualType);
break; break;
case NodeKind.SELECT: case NodeKind.TERNARY:
expr = this.compileSelectExpression(<SelectExpression>expression, contextualType); expr = this.compileTernaryExpression(<TernaryExpression>expression, contextualType);
break; break;
case NodeKind.UNARYPOSTFIX: case NodeKind.UNARYPOSTFIX:
@ -1482,6 +1482,7 @@ export class Compiler extends DiagnosticEmitter {
/** Compiles a call to a function. If an instance method, `this` is the first element in `argumentExpressions`. */ /** Compiles a call to a function. If an instance method, `this` is the first element in `argumentExpressions`. */
compileCall(functionInstance: Function, argumentExpressions: Expression[], reportNode: Node): ExpressionRef { compileCall(functionInstance: Function, argumentExpressions: Expression[], reportNode: Node): ExpressionRef {
const previousType: Type = this.currentType;
// validate and compile arguments // validate and compile arguments
const parameters: Parameter[] = functionInstance.parameters; const parameters: Parameter[] = functionInstance.parameters;
@ -1627,6 +1628,7 @@ export class Compiler extends DiagnosticEmitter {
return this.module.createHost(HostOp.GrowMemory, null, operands); return this.module.createHost(HostOp.GrowMemory, null, operands);
case "unreachable": case "unreachable":
this.currentType = previousType;
return this.module.createUnreachable(); return this.module.createUnreachable();
case "isNaN": // value != value case "isNaN": // value != value
@ -1645,35 +1647,35 @@ export class Compiler extends DiagnosticEmitter {
} }
break; break;
case "isFinite": // value != value ? false : abs(value) != Infinity case "isFinite": // v=[abs(value) != Infinity, false]; return value == value ? v[0] : v[1]
if (functionInstance.typeArguments[0] == Type.f64) { if (functionInstance.typeArguments[0] == Type.f64) {
tempLocal = this.currentFunction.addLocal(Type.f64); tempLocal = this.currentFunction.addLocal(Type.f64);
return this.module.createSelect( return this.module.createSelect(
this.module.createBinary(BinaryOp.NeF64,
this.module.createTeeLocal(tempLocal.index, operands[0]),
this.module.createGetLocal(tempLocal.index, NativeType.F64)
),
this.module.createI32(0),
this.module.createBinary(BinaryOp.NeF64, this.module.createBinary(BinaryOp.NeF64,
this.module.createUnary(UnaryOp.AbsF64, this.module.createUnary(UnaryOp.AbsF64,
this.module.createGetLocal(tempLocal.index, NativeType.F64) this.module.createTeeLocal(tempLocal.index, operands[0])
), ),
this.module.createF64(Infinity) this.module.createF64(Infinity)
),
this.module.createI32(0),
this.module.createBinary(BinaryOp.EqF64,
this.module.createGetLocal(tempLocal.index, NativeType.F64),
this.module.createGetLocal(tempLocal.index, NativeType.F64)
) )
); );
} else if (functionInstance.typeArguments[0] == Type.f32) { } else if (functionInstance.typeArguments[0] == Type.f32) {
tempLocal = this.currentFunction.addLocal(Type.f32); tempLocal = this.currentFunction.addLocal(Type.f32);
return this.module.createSelect( return this.module.createSelect(
this.module.createBinary(BinaryOp.NeF32,
this.module.createTeeLocal(tempLocal.index, operands[0]),
this.module.createGetLocal(tempLocal.index, NativeType.F32)
),
this.module.createI32(0),
this.module.createBinary(BinaryOp.NeF32, this.module.createBinary(BinaryOp.NeF32,
this.module.createUnary(UnaryOp.AbsF32, this.module.createUnary(UnaryOp.AbsF32,
this.module.createGetLocal(tempLocal.index, NativeType.F32) this.module.createTeeLocal(tempLocal.index, operands[0])
), ),
this.module.createF32(Infinity) this.module.createF32(Infinity)
),
this.module.createI32(0),
this.module.createBinary(BinaryOp.EqF32,
this.module.createGetLocal(tempLocal.index, NativeType.F32),
this.module.createGetLocal(tempLocal.index, NativeType.F32)
) )
); );
} }
@ -1845,11 +1847,11 @@ export class Compiler extends DiagnosticEmitter {
throw new Error("not implemented"); throw new Error("not implemented");
} }
compileSelectExpression(expression: SelectExpression, contextualType: Type): ExpressionRef { compileTernaryExpression(expression: TernaryExpression, contextualType: Type): ExpressionRef {
const condition: ExpressionRef = this.compileExpression(expression.condition, Type.i32); const condition: ExpressionRef = this.compileExpression(expression.condition, Type.i32);
const ifThen: ExpressionRef = this.compileExpression(expression.ifThen, contextualType); const ifThen: ExpressionRef = this.compileExpression(expression.ifThen, contextualType);
const ifElse: ExpressionRef = this.compileExpression(expression.ifElse, contextualType); const ifElse: ExpressionRef = this.compileExpression(expression.ifElse, contextualType);
return this.module.createSelect(condition, ifThen, ifElse); return this.module.createIf(condition, ifThen, ifElse);
} }
compileUnaryPostfixExpression(expression: UnaryPostfixExpression, contextualType: Type): ExpressionRef { compileUnaryPostfixExpression(expression: UnaryPostfixExpression, contextualType: Type): ExpressionRef {

View File

@ -448,7 +448,7 @@ export class Module {
return _BinaryenReturn(this.ref, expression); return _BinaryenReturn(this.ref, expression);
} }
createSelect(condition: ExpressionRef, ifTrue: ExpressionRef, ifFalse: ExpressionRef): ExpressionRef { createSelect(ifTrue: ExpressionRef, ifFalse: ExpressionRef, condition: ExpressionRef): ExpressionRef {
if (this.noEmit) return 0; if (this.noEmit) return 0;
return _BinaryenSelect(this.ref, condition, ifTrue, ifFalse); return _BinaryenSelect(this.ref, condition, ifTrue, ifFalse);
} }

View File

@ -1456,7 +1456,7 @@ export class Parser extends DiagnosticEmitter {
this.error(DiagnosticCode.The_operand_of_an_increment_or_decrement_operator_must_be_a_variable_or_a_property_access, expr.range); this.error(DiagnosticCode.The_operand_of_an_increment_or_decrement_operator_must_be_a_variable_or_a_property_access, expr.range);
expr = Expression.createUnaryPostfix(token, expr, tn.range(startPos, tn.pos)); expr = Expression.createUnaryPostfix(token, expr, tn.range(startPos, tn.pos));
// SelectExpression // TernaryExpression
} else if (token == Token.QUESTION) { } else if (token == Token.QUESTION) {
const ifThen: Expression | null = this.parseExpression(tn); const ifThen: Expression | null = this.parseExpression(tn);
if (!ifThen) if (!ifThen)
@ -1465,7 +1465,7 @@ export class Parser extends DiagnosticEmitter {
const ifElse: Expression | null = this.parseExpression(tn); const ifElse: Expression | null = this.parseExpression(tn);
if (!ifElse) if (!ifElse)
return null; return null;
expr = Expression.createSelect(<Expression>expr, <Expression>ifThen, <Expression>ifElse, tn.range(startPos, tn.pos)); expr = Expression.createTernary(<Expression>expr, <Expression>ifThen, <Expression>ifElse, tn.range(startPos, tn.pos));
} else { } else {
this.error(DiagnosticCode._0_expected, tn.range(), ":"); this.error(DiagnosticCode._0_expected, tn.range(), ":");
return null; return null;

View File

@ -121,3 +121,14 @@ sizeof<f64>();
i = load<i32>(4); i = load<i32>(4);
store<i32>(4, i); store<i32>(4, i);
if (NaN == NaN)
unreachable();
if (!isNaN<f32>(NaN))
unreachable();
if (isFinite<f32>(NaN))
unreachable();
if (isFinite<f32>(Infinity))
unreachable();
if (!isFinite<f64>(0))
unreachable();

View File

@ -19,6 +19,10 @@
(local $5 f64) (local $5 f64)
(local $6 f64) (local $6 f64)
(local $7 f64) (local $7 f64)
(local $8 f32)
(local $9 f32)
(local $10 f32)
(local $11 f64)
(drop (drop
(i32.clz (i32.clz
(i32.const 1) (i32.const 1)
@ -191,17 +195,17 @@
) )
(drop (drop
(select (select
(i32.const 0)
(f32.ne (f32.ne
(f32.abs (f32.abs
(get_local $1) (tee_local $1
(f32.const 1.25)
)
) )
(f32.const inf) (f32.const inf)
) )
(f32.ne (i32.const 0)
(tee_local $1 (f32.eq
(f32.const 1.25) (get_local $1)
)
(get_local $1) (get_local $1)
) )
) )
@ -270,17 +274,17 @@
) )
(set_global $builtins/b (set_global $builtins/b
(select (select
(i32.const 0)
(f32.ne (f32.ne
(f32.abs (f32.abs
(get_local $3) (tee_local $3
(f32.const 1.25)
)
) )
(f32.const inf) (f32.const inf)
) )
(f32.ne (i32.const 0)
(tee_local $3 (f32.eq
(f32.const 1.25) (get_local $3)
)
(get_local $3) (get_local $3)
) )
) )
@ -355,17 +359,17 @@
) )
(drop (drop
(select (select
(i32.const 0)
(f64.ne (f64.ne
(f64.abs (f64.abs
(get_local $5) (tee_local $5
(f64.const 1.25)
)
) )
(f64.const inf) (f64.const inf)
) )
(f64.ne (i32.const 0)
(tee_local $5 (f64.eq
(f64.const 1.25) (get_local $5)
)
(get_local $5) (get_local $5)
) )
) )
@ -434,17 +438,17 @@
) )
(set_global $builtins/b (set_global $builtins/b
(select (select
(i32.const 0)
(f64.ne (f64.ne
(f64.abs (f64.abs
(get_local $7) (tee_local $7
(f64.const 1.25)
)
) )
(f64.const inf) (f64.const inf)
) )
(f64.ne (i32.const 0)
(tee_local $7 (f64.eq
(f64.const 1.25) (get_local $7)
)
(get_local $7) (get_local $7)
) )
) )
@ -523,6 +527,80 @@
(i32.const 4) (i32.const 4)
(get_global $builtins/i) (get_global $builtins/i)
) )
(if
(f64.eq
(f64.const nan:0x8000000000000)
(f64.const nan:0x8000000000000)
)
(unreachable)
)
(if
(i32.eqz
(f32.ne
(tee_local $8
(f32.const nan:0x400000)
)
(get_local $8)
)
)
(unreachable)
)
(if
(select
(f32.ne
(f32.abs
(tee_local $9
(f32.const nan:0x400000)
)
)
(f32.const inf)
)
(i32.const 0)
(f32.eq
(get_local $9)
(get_local $9)
)
)
(unreachable)
)
(if
(select
(f32.ne
(f32.abs
(tee_local $10
(f32.const inf)
)
)
(f32.const inf)
)
(i32.const 0)
(f32.eq
(get_local $10)
(get_local $10)
)
)
(unreachable)
)
(if
(i32.eqz
(select
(f64.ne
(f64.abs
(tee_local $11
(f64.const 0)
)
)
(f64.const inf)
)
(i32.const 0)
(f64.eq
(get_local $11)
(get_local $11)
)
)
)
(unreachable)
)
) )
) )
(; (;

View File

@ -0,0 +1,5 @@
let a: i32;
a = 0 ? unreachable() : 1;
a = 1 ? 1 : unreachable();
a = (0 ? unreachable() : 1) ? 1 : unreachable();

View File

@ -0,0 +1,64 @@
(module
(type $v (func))
(global $ternary/a (mut i32) (i32.const 0))
(memory $0 1)
(data (i32.const 4) "\08\00\00\00")
(export "memory" (memory $0))
(start $start)
(func $start (; 0 ;) (type $v)
(set_global $ternary/a
(if (result i32)
(i32.const 0)
(unreachable)
(i32.const 1)
)
)
(set_global $ternary/a
(if (result i32)
(i32.const 1)
(i32.const 1)
(unreachable)
)
)
(set_global $ternary/a
(if (result i32)
(if (result i32)
(i32.const 0)
(unreachable)
(i32.const 1)
)
(i32.const 1)
(unreachable)
)
)
)
)
(;
[program.elements]
clz
ctz
popcnt
rotl
rotr
abs
ceil
copysign
floor
max
min
nearest
sqrt
trunc
current_memory
grow_memory
unreachable
isNaN
isFinite
assert
sizeof
load
store
ternary/a
[program.exports]
;)