From 2e5f4c8da54961707025649d4c8c6840ae32deb0 Mon Sep 17 00:00:00 2001
From: "C. Scott Ananian"
Date: Thu, 6 Jun 2013 16:03:53 -0400
Subject: [PATCH] asm-llvm: parse/validate function table dereferences.
These are a bit tricky because they are all forward references, but we've
already done the hard part. We just return a new TableBinding type which
lets us defer typechecking (and codegen) until we see the call arguments.
We can then infer a type for the function table and check it for consistency.
---
asm-llvm.js | 97 +++++++++++++++++++++++++++-------------
asm-tests/example-asm.js | 2 +-
2 files changed, 68 insertions(+), 31 deletions(-)
diff --git a/asm-llvm.js b/asm-llvm.js
index fdfe389..a391b39 100644
--- a/asm-llvm.js
+++ b/asm-llvm.js
@@ -1524,9 +1524,9 @@ define([], function asm_llvm() {
// Function table references propagate some type information
// so that we can infer the type before seeing the declaration.
- var TableBinding = function(base, size, temp) {
+ var TableBinding = function(base, index, temp) {
this.base = base;
- this.size = size;
+ this.index = index;
this.type = base.type;
this.temp = temp || gensym();
};
@@ -1868,7 +1868,7 @@ define([], function asm_llvm() {
} else if (property.andy) {
// Try to parse as a function table lookup:
// `x:Identifier[index:Expression & n:NumericLiteral](arg:Expression,...)`
- raise(startPos, "unimplemented");
+ return TableBinding.New(base, property);
} else {
raise(startPos, "bad member expression");
}
@@ -1888,37 +1888,62 @@ define([], function asm_llvm() {
var startPos = lastStart;
var callArguments = parseExprList(_parenR, false);
// Type check the call based on the argument types.
- var binding;
- if (base.type===Types.Function) {
- callArguments.forEach(function(b, i) {
+ var binding, ty;
+ var argTypes = callArguments.map(function(b){
+ return defcheck(b).type;
+ });
+ // If this is a forward reference to a future function,
+ // infer the function type based on 'plusCoerced' (the
+ // surrounding context) and the argument types.
+ var makeFunctionTypeFromContext = function() {
+ return Types.Arrow(argTypes.map(function(ty) {
+ // All argument types must be either 'double' or 'int'.
+ if (ty.isSubtypeOf(Types.Double)) {
+ return Types.Double;
+ } else if (ty.isSubtypeOf(Types.Int)) {
+ return Types.Int;
+ } else {
+ raise(startPos, "function arguments must be "+
+ "coerced to either double or int");
+ }
+ // Return type is either 'doublish' or 'intish'
+ }), plusCoerced ? Types.Doublish : Types.Intish);
+ };
+ if (TableBinding.hasInstance(base)) {
+ // Handle an indirect invocation through a function table.
+ if (!powerOf2(base.index.andAmount+1)) {
+ raise(startPos,
+ "function table size must be a power of 2");
+ }
+ if (base.base.type === Types.ForwardReference) {
+ base.base.type =
+ Types.Table(makeFunctionTypeFromContext(),
+ base.index.andAmount+1);
+ }
+ if (!base.base.type.table) {
+ raise(startPos, "base should be function table");
+ }
+ if (base.base.type.size !== (base.index.andAmount+1)) {
+ raise(startPos, "function table size mismatch");
+ }
+ ty = base.base.type.base.apply(argTypes);
+ if (ty===null) {
+ raise(startPos, "call argument mismatch; wants "+
+ base.type.base.toString());
+ }
+ binding = TempBinding.New(ty);
+ } else if (base.type===Types.Function) {
+ // Handle foreign function invocation.
+ argTypes.forEach(function(type, i) {
var msg = "argument "+i+" to foreign function";
- defcheck(b, msg);
- typecheck(b.type, Types.Extern, msg, startPos);
+ typecheck(type, Types.Extern, msg, startPos);
});
binding = TempBinding.New(Types.Unknown);
} else if (base.type.arrow || base.type.functiontypes ||
base.type === Types.ForwardReference) {
- var argTypes = callArguments.map(function(b){
- return defcheck(b).type;
- });
- var ty;
- // If this is a forward reference to a future function,
- // infer the function type based on 'plusCoerced' (the
- // surrounding context) and the argument types.
+ // Handle direct invocation of local function.
if (base.type === Types.ForwardReference) {
- // All argument types must be either 'double' or 'int'.
- var nt = Types.Arrow(argTypes.map(function(ty) {
- if (ty.isSubtypeOf(Types.Double)) {
- return Types.Double;
- } else if (ty.isSubtypeOf(Types.Int)) {
- return Types.Int;
- } else {
- raise(startPos, "function arguments must be "+
- "coerced to either double or int");
- }
- // Return type is either 'doublish' or 'intish'
- }), plusCoerced ? Types.Doublish : Types.Intish);
- base.type = mergeTypes(base.type, nt, startPos);
+ base.type = makeFunctionTypeFromContext();
}
ty = base.type.apply(argTypes);
if (ty===null) {
@@ -1934,7 +1959,11 @@ define([], function asm_llvm() {
};
var parseExprSubscripts = function(plusCoerced) {
- return parseSubscripts(parseExprAtom(), plusCoerced);
+ var b = parseSubscripts(parseExprAtom(), plusCoerced);
+ if (TableBinding.hasInstance(b)) {
+ raise(tokStart, "incomplete function table lookup");
+ }
+ return b;
};
// Parse unary operators, both prefix and postfix.
@@ -2578,7 +2607,15 @@ define([], function asm_llvm() {
raise(yPos, "Function table length is not a power of 2.");
}
ty = Types.Table(lastType, table.length);
- module.env.bind(x, GlobalBinding.New(ty, false), startPos);
+ var binding = module.env.lookup(x);
+ if (binding === null || !binding.pending) {
+ module.env.bind(x, GlobalBinding.New(ty, false), startPos);
+ } else {
+ console.assert(GlobalBinding.hasInstance(binding) &&
+ !binding.mutable);
+ binding.type = mergeTypes(binding.type, ty, startPos);
+ binding.pending = false; // binding is now authoritative.
+ }
module.seenTable = true;
} else if (module.seenTable) {
raise(tokStart, "expected function table");
diff --git a/asm-tests/example-asm.js b/asm-tests/example-asm.js
index 4ab59f0..705fb90 100644
--- a/asm-tests/example-asm.js
+++ b/asm-tests/example-asm.js
@@ -50,7 +50,7 @@ function mymodule(stdlib, foreign, heap) {
i = i|0;
x = x|0;
H32[i>>2] = x; // shifted by log2(byte count)
- //ftable_2[(x-2)&1](); // dynamic call of functions in table 2
+ ftable_2[(x-2)&1](); // dynamic call of functions in table 2
// no return necessary when return type is void
}