diff --git a/src/compiler.ts b/src/compiler.ts index a794c40c..eba1c222 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -441,9 +441,9 @@ export class Compiler extends DiagnosticEmitter { this.compileEnum(element); } - compileEnum(element: Enum): void { + compileEnum(element: Enum): bool { if (element.isCompiled) - return; + return true; var previousValue: EnumValue | null = null; if (element.members) @@ -500,7 +500,7 @@ export class Compiler extends DiagnosticEmitter { throw new Error("declaration expected"); previousValue = val; } - element.isCompiled = true; + return element.isCompiled = true; } // functions @@ -2104,144 +2104,137 @@ export class Compiler extends DiagnosticEmitter { } compileAssignment(expression: Expression, valueExpression: Expression, contextualType: Type): ExpressionRef { - var element: Element | null = null; - switch (expression.kind) { - - case NodeKind.IDENTIFIER: - element = this.program.resolveIdentifier(expression, this.currentFunction); // reports - break; - - case NodeKind.PROPERTYACCESS: - element = this.program.resolvePropertyAccess(expression, this.currentFunction); // reports - break; - - default: - this.error(DiagnosticCode.Operation_not_supported, expression.range); - } - if (!element) + var resolved = this.program.resolveExpression(expression, this.currentFunction); // reports + if (!resolved) return this.module.createUnreachable(); - var type: Type | null = null; + var element = resolved.element; + var elementType: Type; switch (element.kind) { case ElementKind.LOCAL: - type = (element).type; + elementType = (element).type; break; case ElementKind.GLOBAL: - if (this.compileGlobal(element)) - type = (element).type; + if (!this.compileGlobal(element)) // reports + return this.module.createUnreachable(); + assert((element).type != null); + elementType = (element).type; + break; + + case ElementKind.FIELD: + elementType = (element).type; break; case ElementKind.PROPERTY: var setterPrototype = (element).setterPrototype; if (setterPrototype) { var setterInstance = setterPrototype.resolve(); // reports - if (setterInstance) { - if (contextualType == Type.void) { // just set if dropped anyway - return this.compileCall(setterInstance, [ valueExpression ], expression); - } else { // otherwise do a set followed by a get - var getterPrototype = (element).getterPrototype; - if (getterPrototype) { - var getterInstance = getterPrototype.resolve(); // reports - if (getterInstance) { - return this.module.createBlock(null, [ - this.compileCall(setterInstance, [ valueExpression ], expression), - this.compileCall(getterInstance, [], expression) - ], getterInstance.returnType.toNativeType()); - } - } else - this.error(DiagnosticCode.Property_0_does_not_exist_on_type_1, expression.range, (element).simpleName, (element).parent.internalName); - } - } - } else - this.error(DiagnosticCode.Property_0_does_not_exist_on_type_1, expression.range, (element).simpleName, (element).parent.internalName); + if (!setterInstance) + return this.module.createUnreachable(); + elementType = setterInstance.parameters[0].type; + break; + } + this.error(DiagnosticCode.Cannot_assign_to_0_because_it_is_a_constant_or_a_read_only_property, expression.range, (element).internalName); return this.module.createUnreachable(); default: this.error(DiagnosticCode.Operation_not_supported, expression.range); + return this.module.createUnreachable(); } - if (!type) + if (!elementType) return this.module.createUnreachable(); - this.currentType = type; - return this.compileAssignmentWithValue(expression, this.compileExpression(valueExpression, type, ConversionKind.IMPLICIT), contextualType != Type.void); + this.currentType = elementType; + return this.compileAssignmentWithValue(expression, this.compileExpression(valueExpression, elementType, ConversionKind.IMPLICIT), contextualType != Type.void); } compileAssignmentWithValue(expression: Expression, valueWithCorrectType: ExpressionRef, tee: bool = false): ExpressionRef { - var element: Element | null = null; - switch (expression.kind) { - - case NodeKind.IDENTIFIER: - element = this.program.resolveIdentifier(expression, this.currentFunction); - break; - - case NodeKind.PROPERTYACCESS: - element = this.program.resolvePropertyAccess(expression, this.currentFunction); - break; - - default: - this.error(DiagnosticCode.Operation_not_supported, expression.range); - } - if (!element) + var resolved = this.program.resolveExpression(expression, this.currentFunction); // reports + if (!resolved) return this.module.createUnreachable(); - if (element.kind == ElementKind.LOCAL) { - assert((element).type != null); - if (tee) { - this.currentType = (element).type; - return this.module.createTeeLocal((element).index, valueWithCorrectType); - } - this.currentType = Type.void; - return this.module.createSetLocal((element).index, valueWithCorrectType); - } + var element = resolved.element; + switch (element.kind) { - if (element.kind == ElementKind.GLOBAL) { - if (!this.compileGlobal(element)) - return this.module.createUnreachable(); - this.currentType = (element).type; - if (!(element).isMutable) { - this.error(DiagnosticCode.Cannot_assign_to_0_because_it_is_a_constant_or_a_read_only_property, expression.range, element.internalName); - return this.module.createUnreachable(); - } - if (tee) { + case ElementKind.LOCAL: + this.currentType = select((element).type, Type.void, tee); + if ((element).isConstant) { + this.error(DiagnosticCode.Cannot_assign_to_0_because_it_is_a_constant_or_a_read_only_property, expression.range, (element).internalName); + return this.module.createUnreachable(); + } + return tee + ? this.module.createTeeLocal((element).index, valueWithCorrectType) + : this.module.createSetLocal((element).index, valueWithCorrectType); + + case ElementKind.GLOBAL: + if (!this.compileGlobal(element)) + return this.module.createUnreachable(); + assert((element).type != null); + this.currentType = select((element).type, Type.void, tee); + if ((element).isConstant) { + this.error(DiagnosticCode.Cannot_assign_to_0_because_it_is_a_constant_or_a_read_only_property, expression.range, (element).internalName); + return this.module.createUnreachable(); + } + if (!tee) + return this.module.createSetGlobal((element).internalName, valueWithCorrectType); var globalNativeType = ((element).type).toNativeType(); - return this.module.createBlock(null, [ // teeGlobal + return this.module.createBlock(null, [ // emulated teeGlobal this.module.createSetGlobal((element).internalName, valueWithCorrectType), this.module.createGetGlobal((element).internalName, globalNativeType) ], globalNativeType); - } - this.currentType = Type.void; - return this.module.createSetGlobal((element).internalName, valueWithCorrectType); + + case ElementKind.FIELD: + if ((element).prototype.isReadonly) { + this.error(DiagnosticCode.Cannot_assign_to_0_because_it_is_a_constant_or_a_read_only_property, expression.range, (element).internalName); + return this.module.createUnreachable(); + } + assert(resolved.targetExpression != null); + var targetExpr = this.compileExpression(resolved.targetExpression, Type.usize32); + this.currentType = select((element).type, Type.void, tee); + var elementNativeType = (element).type.toNativeType(); + if (!tee) + return this.module.createStore((element).type.byteSize, targetExpr, valueWithCorrectType, elementNativeType, (element).memoryOffset); + var tempLocal = this.currentFunction.getAndFreeTempLocal((element).type); + return this.module.createBlock(null, [ // TODO: simplify if valueWithCorrectType has no side effects + this.module.createSetLocal(tempLocal.index, valueWithCorrectType), + this.module.createStore((element).type.byteSize, targetExpr, this.module.createGetLocal(tempLocal.index, elementNativeType), elementNativeType, (element).memoryOffset), + this.module.createGetLocal(tempLocal.index, elementNativeType) + ], elementNativeType); + + case ElementKind.PROPERTY: + var setterPrototype = (element).setterPrototype; + if (setterPrototype) { + var setterInstance = setterPrototype.resolve(); // reports + if (setterInstance) { + if (!tee) + return this.makeCall(setterInstance, [ valueWithCorrectType ]); + var getterPrototype = (element).getterPrototype; + assert(getterPrototype != null); + var getterInstance = (getterPrototype).resolve(); // reports + if (getterInstance) + return this.module.createBlock(null, [ + this.makeCall(setterInstance, [ valueWithCorrectType ]), + this.makeCall(getterInstance) + ], getterInstance.returnType.toNativeType()); + } + } else + this.error(DiagnosticCode.Cannot_assign_to_0_because_it_is_a_constant_or_a_read_only_property, expression.range, (element).internalName); + return this.module.createUnreachable(); + + default: + this.error(DiagnosticCode.Operation_not_supported, expression.range); + return this.module.createUnreachable(); } - - // TODO: fields, (setters) - - throw new Error("not implemented"); } compileCallExpression(expression: CallExpression, contextualType: Type): ExpressionRef { - var element: Element | null = null; - var thisExpression: Expression; - switch (expression.expression.kind) { - // case NodeKind.THIS: - // case NodeKind.SUPER: - - case NodeKind.IDENTIFIER: - element = this.program.resolveIdentifier(thisExpression = expression.expression, this.currentFunction); - break; - - case NodeKind.PROPERTYACCESS: - element = this.program.resolvePropertyAccess(expression.expression, this.currentFunction); - thisExpression = (expression.expression).expression; - break; - - default: - throw new Error("not implemented"); - } - if (!element) + var resolved = this.program.resolveExpression(expression.expression, this.currentFunction); // reports + if (!resolved) return this.module.createUnreachable(); + var element = resolved.element; if (element.kind == ElementKind.FUNCTION_PROTOTYPE) { var functionPrototype = element; var functionInstance: Function | null = null; @@ -2283,8 +2276,10 @@ export class Compiler extends DiagnosticEmitter { var argumentIndex = 0; var args = new Array(numArgumentsInclThis); - if (functionInstance.instanceMethodOf) - args[argumentIndex++] = thisExpression; + if (functionInstance.instanceMethodOf) { + assert(resolved.targetExpression != null); + args[argumentIndex++] = resolved.targetExpression; + } for (i = 0; i < numArguments; ++i) args[argumentIndex++] = expression.arguments[i]; return this.compileCall(functionInstance, args, expression); @@ -2341,9 +2336,12 @@ export class Compiler extends DiagnosticEmitter { } this.currentType = functionInstance.returnType; + return this.makeCall(functionInstance, operands); + } - if (!functionInstance.isCompiled) - this.compileFunction(functionInstance); + private makeCall(functionInstance: Function, operands: ExpressionRef[] | null = null): ExpressionRef { + if (!(functionInstance.isCompiled || this.compileFunction(functionInstance))) + return this.module.createUnreachable(); // imported function if (functionInstance.isDeclared) @@ -2354,31 +2352,15 @@ export class Compiler extends DiagnosticEmitter { } compileElementAccessExpression(expression: ElementAccessExpression, contextualType: Type): ExpressionRef { - var targetExpression = expression.expression; - var target: Element | null = null; - switch (targetExpression.kind) { - - // case NodeKind.THIS: - - case NodeKind.IDENTIFIER: - target = this.program.resolveIdentifier(targetExpression, this.currentFunction); - break; - - case NodeKind.PROPERTYACCESS: - target = this.program.resolvePropertyAccess(targetExpression, this.currentFunction); - break; - - // case NodeKind.ELEMENTACCESS: - - default: - this.error(DiagnosticCode.Operation_not_supported, expression.range); - } - if (!target) + var resolved = this.program.resolveElementAccess(expression, this.currentFunction); // reports + if (!resolved) return this.module.createUnreachable(); - throw new Error("not implemented"); + + throw new Error("not implemented"); // TODO } compileIdentifierExpression(expression: IdentifierExpression, contextualType: Type): ExpressionRef { + // check special keywords first switch (expression.kind) { case NodeKind.NULL: @@ -2406,22 +2388,32 @@ export class Compiler extends DiagnosticEmitter { case NodeKind.THIS: if (this.currentFunction.instanceMethodOf) { this.currentType = this.currentFunction.instanceMethodOf.type; - return this.module.createGetLocal(0, this.options.target == Target.WASM64 ? NativeType.I64 : NativeType.I32); + return this.module.createGetLocal(0, this.currentType.toNativeType()); } + this.currentType = select(Type.usize64, Type.usize32, this.options.target == Target.WASM64); this.error(DiagnosticCode._this_cannot_be_referenced_in_current_location, expression.range); - this.currentType = this.options.target == Target.WASM64 ? Type.u64 : Type.u32; + return this.module.createUnreachable(); + + case NodeKind.SUPER: + if (this.currentFunction.instanceMethodOf && this.currentFunction.instanceMethodOf.base) { + this.currentType = this.currentFunction.instanceMethodOf.base.type; + return this.module.createGetLocal(0, this.currentType.toNativeType()); + } + this.currentType = select(Type.usize64, Type.usize32, this.options.target == Target.WASM64); + this.error(DiagnosticCode._super_can_only_be_referenced_in_a_derived_class, expression.range); return this.module.createUnreachable(); } - var element = this.program.resolveElement(expression, this.currentFunction); // reports - if (!element) + // otherwise resolve + var resolved = this.program.resolveIdentifier(expression, this.currentFunction); // reports + if (!resolved) return this.module.createUnreachable(); + var element = resolved.element; switch (element.kind) { case ElementKind.LOCAL: - assert((element).type != null); - this.currentType = (element).type; + this.currentType = (element).type; if ((element).hasConstantValue) return makeInlineConstant(element, this.module); assert((element).index >= 0); @@ -2430,23 +2422,16 @@ export class Compiler extends DiagnosticEmitter { case ElementKind.GLOBAL: if (element.isBuiltIn) return compileBuiltinGetConstant(this, element); - - var global = element; - if (!this.compileGlobal(global)) // reports + if (!this.compileGlobal(element)) // reports return this.module.createUnreachable(); - assert(global.type != null); // has been resolved when compileGlobal succeeds - this.currentType = global.type; - if (global.hasConstantValue) - return makeInlineConstant(global, this.module); - else - return this.module.createGetGlobal((element).internalName, this.currentType.toNativeType()); - - // case ElementKind.FIELD - - default: - this.error(DiagnosticCode.Operation_not_supported, expression.range); - return this.module.createUnreachable(); + assert((element).type != null); + this.currentType = (element).type; + if ((element).hasConstantValue) + return makeInlineConstant(element, this.module); + return this.module.createGetGlobal((element).internalName, this.currentType.toNativeType()); } + this.error(DiagnosticCode.Operation_not_supported, expression.range); + return this.module.createUnreachable(); } compileLiteralExpression(expression: LiteralExpression, contextualType: Type): ExpressionRef { @@ -2502,124 +2487,50 @@ export class Compiler extends DiagnosticEmitter { } compilePropertyAccessExpression(propertyAccess: PropertyAccessExpression, contextualType: Type): ExpressionRef { - var expression = propertyAccess.expression; - var propertyName = propertyAccess.property.name; - - // the lhs expression is either 'this', 'super', an identifier or another property access - var target: Element | null; - switch (expression.kind) { - - case NodeKind.THIS: - if (!this.currentFunction.instanceMethodOf) { - this.error(DiagnosticCode._this_cannot_be_referenced_in_current_location, expression.range); - return this.module.createUnreachable(); - } - target = this.currentFunction.instanceMethodOf; - break; - - case NodeKind.SUPER: - if (!(this.currentFunction.instanceMethodOf && this.currentFunction.instanceMethodOf.base)) { - this.error(DiagnosticCode._super_can_only_be_referenced_in_a_derived_class, expression.range); - return this.module.createUnreachable(); - } - target = this.currentFunction.instanceMethodOf.base; - break; - - case NodeKind.IDENTIFIER: - target = this.program.resolveIdentifier(expression, this.currentFunction); // reports - break; - - case NodeKind.PROPERTYACCESS: - target = this.program.resolvePropertyAccess(expression, this.currentFunction); // reports - break; - - default: - throw new Error("lhs expression expected"); - } - if (!target) + var resolved = this.program.resolvePropertyAccess(propertyAccess, this.currentFunction); // reports + if (!resolved) return this.module.createUnreachable(); - // look up the property within the target to obtain the actual element - var element: Element | null; - switch (target.kind) { - - case ElementKind.LOCAL: - assert((target).type != null); - element = ((target).type).classType; - if (!element) { - this.error(DiagnosticCode.Property_0_does_not_exist_on_type_1, propertyAccess.property.range, propertyName, ((target).type).toString()); - return this.module.createUnreachable(); - } - target = element; - break; - - case ElementKind.GLOBAL: - if (!this.compileGlobal(target)) - return this.module.createUnreachable(); - element = ((target).type).classType; - if (!element) { - this.error(DiagnosticCode.Property_0_does_not_exist_on_type_1, propertyAccess.property.range, propertyName, ((target).type).toString()); - return this.module.createUnreachable(); - } - target = element; - break; - - default: - if (target.members) { - element = target.members.get(propertyName); - if (!element) { - this.error(DiagnosticCode.Property_0_does_not_exist_on_type_1, propertyAccess.property.range, propertyName, target.internalName); - return this.module.createUnreachable(); - } - - // handle enum values right away - if (element.kind == ElementKind.ENUMVALUE) { - this.currentType = Type.i32; - if ((element).hasConstantValue) - return this.module.createI32((element).constantValue); - this.compileEnum((element).enum); - return this.module.createGetGlobal((element).internalName, NativeType.I32); - } - } else { - this.error(DiagnosticCode.Property_0_does_not_exist_on_type_1, propertyAccess.property.range, propertyName, target.internalName); - return this.module.createUnreachable(); - } - break; - } - - // handle the element + var element = resolved.element; switch (element.kind) { - case ElementKind.LOCAL: - assert((element).type != null); - return this.module.createGetLocal((element).index, (this.currentType = (element).type).toNativeType()); - - case ElementKind.GLOBAL: + case ElementKind.GLOBAL: // static property if (!this.compileGlobal(element)) return this.module.createUnreachable(); assert((element).type != null); this.currentType = (element).type; if ((element).hasConstantValue) return makeInlineConstant(element, this.module); - else - return this.module.createGetGlobal((element).internalName, this.currentType.toNativeType()); + return this.module.createGetGlobal((element).internalName, this.currentType.toNativeType()); - case ElementKind.PROPERTY: // getter - var getterPrototype = (element).getterPrototype; - if (getterPrototype) { - var getterInstance = getterPrototype.resolve([], this.currentFunction.contextualTypeArguments); - if (getterInstance) { - return this.compileCall(getterInstance, [], propertyAccess); - } else { - return this.module.createUnreachable(); - } - } else { - this.error(DiagnosticCode.Property_0_does_not_exist_on_type_1, propertyAccess.property.range, propertyName, target.internalName); + case ElementKind.ENUMVALUE: // enum value + if (!this.compileEnum((element).enum)) return this.module.createUnreachable(); - } + this.currentType = Type.i32; + if ((element).hasConstantValue) + return this.module.createI32((element).constantValue); + return this.module.createGetGlobal((element).internalName, NativeType.I32); + + case ElementKind.FIELD: // instance field + assert(resolved.target != null); + assert(resolved.targetExpression != null); + assert((element).memoryOffset >= 0); + return this.module.createLoad((element).type.byteSize, (element).type.isSignedInteger, + this.compileExpression(resolved.targetExpression, select(Type.usize64, Type.usize32, this.options.target == Target.WASM64)), + (element).type.toNativeType(), + (element).memoryOffset + ); + + case ElementKind.PROPERTY: // instance property (here: getter) + var getter = (element).getterPrototype; + assert(getter != null); + var getterInstance = (getter).resolve(); // reports + if (!getterInstance) + return this.module.createUnreachable(); + return this.compileCall(getterInstance, [], propertyAccess); } this.error(DiagnosticCode.Operation_not_supported, propertyAccess.range); - throw new Error("not implemented"); + return this.module.createUnreachable(); } compileTernaryExpression(expression: TernaryExpression, contextualType: Type): ExpressionRef { diff --git a/src/module.ts b/src/module.ts index 6b1d9804..fc08d820 100644 --- a/src/module.ts +++ b/src/module.ts @@ -496,24 +496,24 @@ export class Module { } } - createCall(target: string, operands: ExpressionRef[], returnType: NativeType): ExpressionRef { + createCall(target: string, operands: ExpressionRef[] | null, returnType: NativeType): ExpressionRef { if (this.noEmit) return 0; var cStr = allocString(target); var cArr = allocI32Array(operands); try { - return _BinaryenCall(this.ref, cStr, cArr, operands.length, returnType); + return _BinaryenCall(this.ref, cStr, cArr, operands && operands.length || 0, returnType); } finally { Heap.dispose(cArr); Heap.dispose(cStr); } } - createCallImport(target: string, operands: ExpressionRef[], returnType: NativeType): ExpressionRef { + createCallImport(target: string, operands: ExpressionRef[] | null, returnType: NativeType): ExpressionRef { if (this.noEmit) return 0; var cStr = allocString(target); var cArr = allocI32Array(operands); try { - return _BinaryenCallImport(this.ref, cStr, cArr, operands.length, returnType); + return _BinaryenCallImport(this.ref, cStr, cArr, operands && operands.length || 0, returnType); } finally { Heap.dispose(cArr); Heap.dispose(cStr); diff --git a/src/program.ts b/src/program.ts index 9055b4b8..98b15e54 100644 --- a/src/program.ts +++ b/src/program.ts @@ -67,7 +67,10 @@ import { hasDecorator, hasModifier, - mangleInternalName + mangleInternalName, + ElementAccessExpression, + ThisExpression, + SuperExpression } from "./ast"; import { @@ -896,11 +899,11 @@ export class Program extends DiagnosticEmitter { } /** Resolves an identifier to the element it refers to. */ - resolveIdentifier(identifier: IdentifierExpression, contextualFunction: Function): Element | null { + resolveIdentifier(identifier: IdentifierExpression, contextualFunction: Function): ResolvedElement | null { var name = identifier.name; var local = contextualFunction.locals.get(name); if (local) - return local; + return resolvedElement.set(local); var element: Element | null; var namespace: Element | null; @@ -909,96 +912,131 @@ export class Program extends DiagnosticEmitter { if (contextualFunction && (namespace = contextualFunction.prototype.namespace)) { do { if (element = this.elements.get(namespace.internalName + STATIC_DELIMITER + name)) - return element; + return resolvedElement.set(element); } while (namespace = namespace.namespace); } // search current file if (element = this.elements.get(identifier.range.source.internalPath + PATH_DELIMITER + name)) - return element; + return resolvedElement.set(element); // search global scope if (element = this.elements.get(name)) - return element; + return resolvedElement.set(element); this.error(DiagnosticCode.Cannot_find_name_0, identifier.range, name); return null; } /** Resolves a property access to the element it refers to. */ - resolvePropertyAccess(propertyAccess: PropertyAccessExpression, contextualFunction: Function): Element | null { - var expression = propertyAccess.expression; - var target: Element | null = null; - switch (expression.kind) { + resolvePropertyAccess(propertyAccess: PropertyAccessExpression, contextualFunction: Function): ResolvedElement | null { + var resolved: ResolvedElement | null; - case NodeKind.IDENTIFIER: - target = this.resolveIdentifier(expression, contextualFunction); - break; - - case NodeKind.PROPERTYACCESS: - target = this.resolvePropertyAccess(expression, contextualFunction); - break; - - // case NodeKind.ELEMENTACCESS: - - default: - throw new Error("property target expected"); - } - if (!target) + // start by resolving the lhs target (expression before the last dot) + var targetExpression = propertyAccess.expression; + if (!(resolved = this.resolveExpression(targetExpression, contextualFunction))) return null; + var target = resolved.element; + // at this point we know exactly what the target is, so look up the element within var propertyName = propertyAccess.property.name; + var targetType: Type | null; switch (target.kind) { case ElementKind.GLOBAL: case ElementKind.LOCAL: - var type = (target).type; - assert(type != null); // locals don't have lazy types, unlike globals - if ((type).classType) { - target = (type).classType; + targetType = (target).type; + if (!targetType) // FIXME: are globals always resolved here? + throw new Error("type expected"); + if (targetType.classType) + target = targetType.classType; // fall-through - } else + else break; default: if (target.members) { var member = target.members.get(propertyName); if (member) - return member; + return resolvedElement.set(member).withTarget(target, targetExpression); } break; } - this.error(DiagnosticCode.Property_0_does_not_exist_on_type_1, propertyAccess.property.range, propertyAccess.property.name, target.internalName); + this.error(DiagnosticCode.Property_0_does_not_exist_on_type_1, propertyAccess.property.range, propertyName, target.internalName); return null; } - resolveElement(expression: Expression, contextualFunction: Function): Element | null { + resolveElementAccess(elementAccess: ElementAccessExpression, contextualFunction: Function): ResolvedElement | null { + var resolved: ResolvedElement | null; - // this -> Class - if (expression.kind == NodeKind.THIS) { - if (contextualFunction.instanceMethodOf) - return contextualFunction.instanceMethodOf; - this.error(DiagnosticCode._this_cannot_be_referenced_in_current_location, expression.range); + // start by resolving the lhs target (expression before the last dot) + var targetExpression = elementAccess.expression; + if (!(resolved = this.resolveExpression(targetExpression, contextualFunction))) return null; - } - - // local or global name - if (expression.kind == NodeKind.IDENTIFIER) { - return this.resolveIdentifier(expression, contextualFunction); - - // static or instance property (incl. enum values) or method - } else if (expression.kind == NodeKind.PROPERTYACCESS) { - return this.resolvePropertyAccess(expression, contextualFunction); - - // instantiation - } else if (expression.kind == NodeKind.NEW) { - return this.resolveElement((expression).expression, contextualFunction); - } + var target = resolved.element; + // at this point we know exactly what the target is, so make sure it is an array and look up the element within throw new Error("not implemented"); } + + resolveExpression(expression: Expression, contextualFunction: Function): ResolvedElement | null { + var classType: Class | null; + switch (expression.kind) { + + case NodeKind.THIS: // -> Class + if (classType = contextualFunction.instanceMethodOf) + return resolvedElement.set(classType); + this.error(DiagnosticCode._this_cannot_be_referenced_in_current_location, expression.range); + return null; + + case NodeKind.SUPER: // -> Class + if ((classType = contextualFunction.instanceMethodOf) && (classType = classType.base)) + return resolvedElement.set(classType); + this.error(DiagnosticCode._super_can_only_be_referenced_in_a_derived_class, expression.range); + return null; + + case NodeKind.IDENTIFIER: + return this.resolveIdentifier(expression, contextualFunction); + + case NodeKind.PROPERTYACCESS: + return this.resolvePropertyAccess(expression, contextualFunction); + + case NodeKind.ELEMENTACCESS: + return this.resolveElementAccess(expression, contextualFunction); + + default: + this.error(DiagnosticCode.Operation_not_supported, expression.range); + return null; + } + } } +/** Common result structure returned when calling any of the resolve functions on a {@link Program}. */ +export class ResolvedElement { + + /** The target element, if a property or element access */ + target: Element | null; + /** The target element's sub-expression, if a property or element access. */ + targetExpression: Expression | null; + /** The element being accessed. */ + element: Element; + + set(element: Element): this { + this.target = null; + this.targetExpression = null; + this.element = element; + return this; + } + + withTarget(target: Element, targetExpression: Expression): this { + this.target = target; + this.targetExpression = targetExpression; + return this; + } +} + +var resolvedElement = new ResolvedElement(); + /** Indicates the specific kind of an {@link Element}. */ export enum ElementKind { /** A {@link Global}. */ @@ -1288,6 +1326,7 @@ export class Local extends VariableLikeElement { kind = ElementKind.LOCAL; + type: Type; // more specific /** Local index. */ index: i32; @@ -1685,6 +1724,7 @@ export class Field extends Element { /** Constructs a new field. */ constructor(prototype: FieldPrototype, internalName: string, type: Type) { super(prototype.program, prototype.simpleName, internalName); + this.prototype = prototype; this.flags = prototype.flags; this.type = type; } diff --git a/tests/compiler.js b/tests/compiler.js index a68603dd..9aef86a9 100644 --- a/tests/compiler.js +++ b/tests/compiler.js @@ -58,7 +58,8 @@ glob.sync(filter, { cwd: __dirname + "/compiler" }).forEach(filename => { module.interpret(); console.log(chalk.default.green("interpret OK")); try { - var wasmModule = new WebAssembly.Module(module.toBinary()); + var binary = module.toBinary(); + var wasmModule = new WebAssembly.Module(binary); var wasmInstance = new WebAssembly.Instance(wasmModule, { env: { externalFunc: function() {}, diff --git a/tests/compiler/class.optimized-inlined.wast b/tests/compiler/class.optimized-inlined.wast index 3af01bf7..1fa7c96a 100644 --- a/tests/compiler/class.optimized-inlined.wast +++ b/tests/compiler/class.optimized-inlined.wast @@ -63,6 +63,33 @@ ) ) ) + (drop + (i32.load + (get_local $0) + ) + ) + (drop + (i32.load16_s offset=4 + (get_local $0) + ) + ) + (drop + (i32.load8_s offset=6 + (get_local $0) + ) + ) + (i32.store + (get_local $0) + (i32.const 1) + ) + (i32.store16 offset=4 + (get_local $0) + (i32.const 2) + ) + (i32.store8 offset=6 + (get_local $0) + (i32.const 3) + ) (get_local $0) ) (func $start (; 1 ;) (type $v) diff --git a/tests/compiler/class.optimized.wast b/tests/compiler/class.optimized.wast index 9fcb30fb..5653be28 100644 --- a/tests/compiler/class.optimized.wast +++ b/tests/compiler/class.optimized.wast @@ -65,6 +65,33 @@ (f32.const 2) ) ) + (drop + (i32.load + (get_local $0) + ) + ) + (drop + (i32.load16_s offset=4 + (get_local $0) + ) + ) + (drop + (i32.load8_s offset=6 + (get_local $0) + ) + ) + (i32.store + (get_local $0) + (i32.const 1) + ) + (i32.store16 offset=4 + (get_local $0) + (i32.const 2) + ) + (i32.store8 offset=6 + (get_local $0) + (i32.const 3) + ) (get_local $0) ) (func $start (; 5 ;) (type $v) diff --git a/tests/compiler/class.ts b/tests/compiler/class.ts index 84a52692..cfa847a4 100644 --- a/tests/compiler/class.ts +++ b/tests/compiler/class.ts @@ -20,6 +20,14 @@ export function test(animal: Animal): Animal { animal.instanceAdd(1, 2); animal.instanceSub(1, 2); + animal.one; + animal.two; + animal.three; + + animal.one = 0 + 1; + animal.two = 1 + 1; + animal.three = 1 + 1 + 1; + var ptr = changetype(animal); var cls = changetype>(ptr); return cls; diff --git a/tests/compiler/class.wast b/tests/compiler/class.wast index 1240cc16..c920f1ee 100644 --- a/tests/compiler/class.wast +++ b/tests/compiler/class.wast @@ -76,6 +76,45 @@ (f32.const 2) ) ) + (drop + (i32.load + (get_local $0) + ) + ) + (drop + (i32.load16_s offset=4 + (get_local $0) + ) + ) + (drop + (i32.load8_s offset=6 + (get_local $0) + ) + ) + (i32.store + (get_local $0) + (i32.add + (i32.const 0) + (i32.const 1) + ) + ) + (i32.store16 offset=4 + (get_local $0) + (i32.add + (i32.const 1) + (i32.const 1) + ) + ) + (i32.store8 offset=6 + (get_local $0) + (i32.add + (i32.add + (i32.const 1) + (i32.const 1) + ) + (i32.const 1) + ) + ) (block (set_local $1 (get_local $0) diff --git a/tests/compiler/empty.optimized.wast b/tests/compiler/empty.optimized.wast new file mode 100644 index 00000000..7a4e7940 --- /dev/null +++ b/tests/compiler/empty.optimized.wast @@ -0,0 +1,4 @@ +(module + (memory $0 1) + (export "memory" (memory $0)) +) diff --git a/tests/compiler/empty.ts b/tests/compiler/empty.ts new file mode 100644 index 00000000..e69de29b diff --git a/tests/compiler/empty.wast b/tests/compiler/empty.wast new file mode 100644 index 00000000..91cd4aaf --- /dev/null +++ b/tests/compiler/empty.wast @@ -0,0 +1,54 @@ +(module + (global $HEAP_BASE i32 (i32.const 4)) + (memory $0 1) + (export "memory" (memory $0)) +) +(; +[program.elements] + GLOBAL: NaN + GLOBAL: Infinity + FUNCTION_PROTOTYPE: isNaN + FUNCTION_PROTOTYPE: isFinite + FUNCTION_PROTOTYPE: clz + FUNCTION_PROTOTYPE: ctz + FUNCTION_PROTOTYPE: popcnt + FUNCTION_PROTOTYPE: rotl + FUNCTION_PROTOTYPE: rotr + FUNCTION_PROTOTYPE: abs + FUNCTION_PROTOTYPE: max + FUNCTION_PROTOTYPE: min + FUNCTION_PROTOTYPE: ceil + FUNCTION_PROTOTYPE: floor + FUNCTION_PROTOTYPE: copysign + FUNCTION_PROTOTYPE: nearest + FUNCTION_PROTOTYPE: reinterpret + FUNCTION_PROTOTYPE: sqrt + FUNCTION_PROTOTYPE: trunc + FUNCTION_PROTOTYPE: load + FUNCTION_PROTOTYPE: store + FUNCTION_PROTOTYPE: sizeof + FUNCTION_PROTOTYPE: select + FUNCTION_PROTOTYPE: unreachable + FUNCTION_PROTOTYPE: current_memory + FUNCTION_PROTOTYPE: grow_memory + FUNCTION_PROTOTYPE: parseInt + FUNCTION_PROTOTYPE: parseFloat + FUNCTION_PROTOTYPE: changetype + FUNCTION_PROTOTYPE: assert + FUNCTION_PROTOTYPE: i8 + FUNCTION_PROTOTYPE: i16 + FUNCTION_PROTOTYPE: i32 + FUNCTION_PROTOTYPE: i64 + FUNCTION_PROTOTYPE: u8 + FUNCTION_PROTOTYPE: u16 + FUNCTION_PROTOTYPE: u32 + FUNCTION_PROTOTYPE: u64 + FUNCTION_PROTOTYPE: bool + FUNCTION_PROTOTYPE: f32 + FUNCTION_PROTOTYPE: f64 + FUNCTION_PROTOTYPE: isize + FUNCTION_PROTOTYPE: usize + GLOBAL: HEAP_BASE +[program.exports] + +;) diff --git a/tests/compiler/fmod.optimized.wast b/tests/compiler/fmod.optimized.wast index b6945be3..2e10f928 100644 --- a/tests/compiler/fmod.optimized.wast +++ b/tests/compiler/fmod.optimized.wast @@ -1,8 +1,10 @@ (module (type $FFF (func (param f64 f64) (result f64))) + (type $fff (func (param f32 f32) (result f32))) (type $v (func)) (memory $0 1) (export "fmod" (func $fmod/fmod)) + (export "fmodf" (func $fmod/fmodf)) (export "memory" (memory $0)) (start $start) (func $fmod/fmod (; 0 ;) (type $FFF) (param $0 f64) (param $1 f64) (result f64) @@ -391,8 +393,374 @@ ) ) ) - (func $start (; 1 ;) (type $v) + (func $fmod/fmodf (; 1 ;) (type $fff) (param $0 f32) (param $1 f32) (result f32) + (local $2 i32) + (local $3 i32) + (local $4 i32) + (local $5 i32) + (local $6 i32) + (local $7 i32) + (local $8 f32) + (set_local $4 + (i32.and + (i32.shr_u + (tee_local $2 + (i32.reinterpret/f32 + (get_local $0) + ) + ) + (i32.const 23) + ) + (i32.const 255) + ) + ) + (set_local $6 + (i32.and + (i32.shr_u + (tee_local $5 + (i32.reinterpret/f32 + (get_local $1) + ) + ) + (i32.const 23) + ) + (i32.const 255) + ) + ) + (set_local $7 + (i32.and + (get_local $2) + (i32.const -2147483648) + ) + ) + (if + (if (result i32) + (if (result i32) + (tee_local $3 + (i32.eqz + (i32.shl + (get_local $5) + (i32.const 1) + ) + ) + ) + (get_local $3) + (tee_local $3 + (f32.ne + (tee_local $8 + (get_local $1) + ) + (get_local $8) + ) + ) + ) + (get_local $3) + (i32.eq + (get_local $4) + (i32.const 255) + ) + ) + (return + (f32.div + (f32.mul + (get_local $0) + (get_local $1) + ) + (f32.mul + (get_local $0) + (get_local $1) + ) + ) + ) + ) + (if + (i32.le_u + (i32.shl + (get_local $2) + (i32.const 1) + ) + (i32.shl + (get_local $5) + (i32.const 1) + ) + ) + (block + (if + (i32.eq + (i32.shl + (get_local $2) + (i32.const 1) + ) + (i32.shl + (get_local $5) + (i32.const 1) + ) + ) + (return + (f32.mul + (f32.const 0) + (get_local $0) + ) + ) + ) + (return + (get_local $0) + ) + ) + ) + (set_local $2 + (if (result i32) + (get_local $4) + (i32.or + (i32.and + (get_local $2) + (i32.const 8388607) + ) + (i32.const 8388608) + ) + (block (result i32) + (set_local $3 + (i32.shl + (get_local $2) + (i32.const 9) + ) + ) + (loop $continue|0 + (if + (i32.eqz + (i32.shr_u + (get_local $3) + (i32.const 31) + ) + ) + (block + (set_local $4 + (i32.sub + (get_local $4) + (i32.const 1) + ) + ) + (set_local $3 + (i32.shl + (get_local $3) + (i32.const 1) + ) + ) + (br $continue|0) + ) + ) + ) + (i32.shl + (get_local $2) + (i32.add + (i32.sub + (i32.const 0) + (get_local $4) + ) + (i32.const 1) + ) + ) + ) + ) + ) + (set_local $5 + (if (result i32) + (get_local $6) + (i32.or + (i32.and + (get_local $5) + (i32.const 8388607) + ) + (i32.const 8388608) + ) + (block (result i32) + (set_local $3 + (i32.shl + (get_local $5) + (i32.const 9) + ) + ) + (loop $continue|1 + (if + (i32.eqz + (i32.shr_u + (get_local $3) + (i32.const 31) + ) + ) + (block + (set_local $6 + (i32.sub + (get_local $6) + (i32.const 1) + ) + ) + (set_local $3 + (i32.shl + (get_local $3) + (i32.const 1) + ) + ) + (br $continue|1) + ) + ) + ) + (i32.shl + (get_local $5) + (i32.add + (i32.sub + (i32.const 0) + (get_local $6) + ) + (i32.const 1) + ) + ) + ) + ) + ) + (loop $continue|2 + (if + (i32.gt_s + (get_local $4) + (get_local $6) + ) + (block + (if + (i32.eqz + (i32.shr_u + (tee_local $3 + (i32.sub + (get_local $2) + (get_local $5) + ) + ) + (i32.const 31) + ) + ) + (block + (if + (i32.eqz + (get_local $3) + ) + (return + (f32.mul + (f32.const 0) + (get_local $0) + ) + ) + ) + (set_local $2 + (get_local $3) + ) + ) + ) + (set_local $2 + (i32.shl + (get_local $2) + (i32.const 1) + ) + ) + (set_local $4 + (i32.sub + (get_local $4) + (i32.const 1) + ) + ) + (br $continue|2) + ) + ) + ) + (if + (i32.eqz + (i32.shr_u + (tee_local $3 + (i32.sub + (get_local $2) + (get_local $5) + ) + ) + (i32.const 31) + ) + ) + (block + (if + (i32.eqz + (get_local $3) + ) + (return + (f32.mul + (f32.const 0) + (get_local $0) + ) + ) + ) + (set_local $2 + (get_local $3) + ) + ) + ) + (loop $continue|3 + (if + (i32.eqz + (i32.shr_u + (get_local $2) + (i32.const 23) + ) + ) + (block + (set_local $4 + (i32.sub + (get_local $4) + (i32.const 1) + ) + ) + (set_local $2 + (i32.shl + (get_local $2) + (i32.const 1) + ) + ) + (br $continue|3) + ) + ) + ) + (f32.reinterpret/i32 + (i32.or + (tee_local $2 + (if (result i32) + (i32.gt_s + (get_local $4) + (i32.const 0) + ) + (i32.or + (i32.sub + (get_local $2) + (i32.const 8388608) + ) + (i32.shl + (get_local $4) + (i32.const 23) + ) + ) + (i32.shr_u + (get_local $2) + (i32.add + (i32.sub + (i32.const 0) + (get_local $4) + ) + (i32.const 1) + ) + ) + ) + ) + (get_local $7) + ) + ) + ) + (func $start (; 2 ;) (type $v) (local $0 f64) + (local $1 f32) (if (f64.eq (tee_local $0 @@ -445,5 +813,57 @@ ) (unreachable) ) + (if + (f32.eq + (tee_local $1 + (call $fmod/fmodf + (f32.const 1) + (f32.const nan:0x400000) + ) + ) + (get_local $1) + ) + (unreachable) + ) + (if + (f32.ne + (call $fmod/fmodf + (f32.const 1.5) + (f32.const 1) + ) + (f32.const 0.5) + ) + (unreachable) + ) + (if + (i32.eqz + (f32.lt + (f32.sub + (call $fmod/fmodf + (f32.const 9.199999809265137) + (f32.const 2) + ) + (f32.const 1.2000000476837158) + ) + (f32.const 1.1920928955078125e-07) + ) + ) + (unreachable) + ) + (if + (i32.eqz + (f32.lt + (f32.sub + (call $fmod/fmodf + (f32.const 9.199999809265137) + (f32.const 3.700000047683716) + ) + (f32.const 1.7999999523162842) + ) + (f32.const 1.1920928955078125e-07) + ) + ) + (unreachable) + ) ) ) diff --git a/tests/compiler/fmod.ts b/tests/compiler/fmod.ts index 786df7f8..76358eba 100644 --- a/tests/compiler/fmod.ts +++ b/tests/compiler/fmod.ts @@ -14,6 +14,7 @@ export function fmod(x: f64, y: f64): f64 { return x; } + // normalize x and y if (!ex) { for (var i = ux << 12; !(i >> 63); i <<= 1) --ex; @@ -31,6 +32,7 @@ export function fmod(x: f64, y: f64): f64 { uy |= 1 << 52; } + // x mod y for (; ex > ey; ex--) { i = ux - uy; if (!(i >> 63)) { @@ -49,6 +51,7 @@ export function fmod(x: f64, y: f64): f64 { for (; !(ux >> 52); ux <<= 1) --ex; + // scale result if (ex > 0) { ux -= 1 << 52; ux |= ex << 52; @@ -63,3 +66,72 @@ assert(isNaN(fmod(1, NaN))); assert(fmod(1.5, 1.0) == 0.5); // exactly 0.5 (as in C) assert(fmod(9.2, 2.0) - 1.2 < f64.EPSILON); // not exactly 1.2 (as in C) assert(fmod(9.2, 3.7) - 1.8 < f64.EPSILON); // not exactly 1.8 (as in C) + +export function fmodf(x: f32, y: f32): f32 { + // the following is based on musl's implementation of fmodf + var ux = reinterpret(x); + var uy = reinterpret(y); + var ex = (ux >> 23 & 0xff); + var ey = (uy >> 23 & 0xff); + var sx = ux & 0x80000000; + + if (uy << 1 == 0 || isNaN(y) || ex == 0xff) + return (x * y) / (x * y); + if (ux << 1 <= uy << 1) { + if (ux << 1 == uy << 1) + return 0 * x; + return x; + } + + // normalize x and y + if (!ex) { + for (var i = ux << 9; !(i >> 31); i <<= 1) + --ex; + ux <<= -ex + 1; + } else { + ux &= -1 >> 9; + ux |= 1 << 23; + } + if (!ey) { + for (i = uy << 9; !(i >> 31); i <<= 1) + --ey; + uy <<= -ey + 1; + } else { + uy &= -1 >> 9; + uy |= 1 << 23; + } + + // x mod y + for (; ex > ey; --ex) { + i = ux - uy; + if (!(i >> 31)) { + if (!i) + return 0 * x; + ux = i; + } + ux <<= 1; + } + i = ux - uy; + if (!(i >> 31)) { + if (!i) + return 0 * x; + ux = i; + } + for (; !(ux >> 23); ux <<= 1) + --ex; + + // scale result + if (ex > 0) { + ux -= 1 << 23; + ux |= ex << 23; + } else { + ux >>= -ex + 1; + } + ux |= sx; + return reinterpret(ux); +} + +assert(isNaN(fmodf(1, NaN))); +assert(fmodf(1.5, 1.0) == 0.5); +assert(fmodf(9.2, 2.0) - 1.2 < f32.EPSILON); +assert(fmodf(9.2, 3.7) - 1.8 < f32.EPSILON); diff --git a/tests/compiler/fmod.wast b/tests/compiler/fmod.wast index 6ed67b2b..b1dec1e7 100644 --- a/tests/compiler/fmod.wast +++ b/tests/compiler/fmod.wast @@ -1,10 +1,13 @@ (module (type $FFF (func (param f64 f64) (result f64))) + (type $fff (func (param f32 f32) (result f32))) (type $v (func)) (global $f64.EPSILON f64 (f64.const 2.220446049250313e-16)) + (global $f32.EPSILON f32 (f32.const 1.1920928955078125e-07)) (global $HEAP_BASE i32 (i32.const 4)) (memory $0 1) (export "fmod" (func $fmod/fmod)) + (export "fmodf" (func $fmod/fmodf)) (export "memory" (memory $0)) (start $start) (func $fmod/fmod (; 0 ;) (type $FFF) (param $0 f64) (param $1 f64) (result f64) @@ -481,8 +484,456 @@ ) ) ) - (func $start (; 1 ;) (type $v) + (func $fmod/fmodf (; 1 ;) (type $fff) (param $0 f32) (param $1 f32) (result f32) + (local $2 i32) + (local $3 i32) + (local $4 i32) + (local $5 i32) + (local $6 i32) + (local $7 f32) + (local $8 i32) + (local $9 i32) + (block + (set_local $2 + (i32.reinterpret/f32 + (get_local $0) + ) + ) + ) + (block + (set_local $3 + (i32.reinterpret/f32 + (get_local $1) + ) + ) + ) + (block + (set_local $4 + (i32.and + (i32.shr_u + (get_local $2) + (i32.const 23) + ) + (i32.const 255) + ) + ) + ) + (block + (set_local $5 + (i32.and + (i32.shr_u + (get_local $3) + (i32.const 23) + ) + (i32.const 255) + ) + ) + ) + (block + (set_local $6 + (i32.and + (get_local $2) + (i32.const -2147483648) + ) + ) + ) + (if + (if (result i32) + (tee_local $8 + (if (result i32) + (tee_local $8 + (i32.eq + (i32.shl + (get_local $3) + (i32.const 1) + ) + (i32.const 0) + ) + ) + (get_local $8) + (f32.ne + (tee_local $7 + (get_local $1) + ) + (get_local $7) + ) + ) + ) + (get_local $8) + (i32.eq + (get_local $4) + (i32.const 255) + ) + ) + (return + (f32.div + (f32.mul + (get_local $0) + (get_local $1) + ) + (f32.mul + (get_local $0) + (get_local $1) + ) + ) + ) + ) + (if + (i32.le_u + (i32.shl + (get_local $2) + (i32.const 1) + ) + (i32.shl + (get_local $3) + (i32.const 1) + ) + ) + (block + (if + (i32.eq + (i32.shl + (get_local $2) + (i32.const 1) + ) + (i32.shl + (get_local $3) + (i32.const 1) + ) + ) + (return + (f32.mul + (f32.const 0) + (get_local $0) + ) + ) + ) + (return + (get_local $0) + ) + ) + ) + (if + (i32.eqz + (get_local $4) + ) + (block + (block $break|0 + (block + (set_local $9 + (i32.shl + (get_local $2) + (i32.const 9) + ) + ) + ) + (loop $continue|0 + (if + (i32.eqz + (i32.shr_u + (get_local $9) + (i32.const 31) + ) + ) + (block + (set_local $4 + (i32.sub + (get_local $4) + (i32.const 1) + ) + ) + (set_local $9 + (i32.shl + (get_local $9) + (i32.const 1) + ) + ) + (br $continue|0) + ) + ) + ) + ) + (set_local $2 + (i32.shl + (get_local $2) + (i32.add + (i32.sub + (i32.const 0) + (get_local $4) + ) + (i32.const 1) + ) + ) + ) + ) + (block + (set_local $2 + (i32.and + (get_local $2) + (i32.shr_u + (i32.sub + (i32.const 0) + (i32.const 1) + ) + (i32.const 9) + ) + ) + ) + (set_local $2 + (i32.or + (get_local $2) + (i32.shl + (i32.const 1) + (i32.const 23) + ) + ) + ) + ) + ) + (if + (i32.eqz + (get_local $5) + ) + (block + (block $break|1 + (set_local $9 + (i32.shl + (get_local $3) + (i32.const 9) + ) + ) + (loop $continue|1 + (if + (i32.eqz + (i32.shr_u + (get_local $9) + (i32.const 31) + ) + ) + (block + (set_local $5 + (i32.sub + (get_local $5) + (i32.const 1) + ) + ) + (set_local $9 + (i32.shl + (get_local $9) + (i32.const 1) + ) + ) + (br $continue|1) + ) + ) + ) + ) + (set_local $3 + (i32.shl + (get_local $3) + (i32.add + (i32.sub + (i32.const 0) + (get_local $5) + ) + (i32.const 1) + ) + ) + ) + ) + (block + (set_local $3 + (i32.and + (get_local $3) + (i32.shr_u + (i32.sub + (i32.const 0) + (i32.const 1) + ) + (i32.const 9) + ) + ) + ) + (set_local $3 + (i32.or + (get_local $3) + (i32.shl + (i32.const 1) + (i32.const 23) + ) + ) + ) + ) + ) + (block $break|2 + (nop) + (loop $continue|2 + (if + (i32.gt_s + (get_local $4) + (get_local $5) + ) + (block + (block + (set_local $9 + (i32.sub + (get_local $2) + (get_local $3) + ) + ) + (if + (i32.eqz + (i32.shr_u + (get_local $9) + (i32.const 31) + ) + ) + (block + (if + (i32.eqz + (get_local $9) + ) + (return + (f32.mul + (f32.const 0) + (get_local $0) + ) + ) + ) + (set_local $2 + (get_local $9) + ) + ) + ) + (set_local $2 + (i32.shl + (get_local $2) + (i32.const 1) + ) + ) + ) + (set_local $4 + (i32.sub + (get_local $4) + (i32.const 1) + ) + ) + (br $continue|2) + ) + ) + ) + ) + (set_local $9 + (i32.sub + (get_local $2) + (get_local $3) + ) + ) + (if + (i32.eqz + (i32.shr_u + (get_local $9) + (i32.const 31) + ) + ) + (block + (if + (i32.eqz + (get_local $9) + ) + (return + (f32.mul + (f32.const 0) + (get_local $0) + ) + ) + ) + (set_local $2 + (get_local $9) + ) + ) + ) + (block $break|3 + (nop) + (loop $continue|3 + (if + (i32.eqz + (i32.shr_u + (get_local $2) + (i32.const 23) + ) + ) + (block + (set_local $4 + (i32.sub + (get_local $4) + (i32.const 1) + ) + ) + (set_local $2 + (i32.shl + (get_local $2) + (i32.const 1) + ) + ) + (br $continue|3) + ) + ) + ) + ) + (if + (i32.gt_s + (get_local $4) + (i32.const 0) + ) + (block + (set_local $2 + (i32.sub + (get_local $2) + (i32.shl + (i32.const 1) + (i32.const 23) + ) + ) + ) + (set_local $2 + (i32.or + (get_local $2) + (i32.shl + (get_local $4) + (i32.const 23) + ) + ) + ) + ) + (set_local $2 + (i32.shr_u + (get_local $2) + (i32.add + (i32.sub + (i32.const 0) + (get_local $4) + ) + (i32.const 1) + ) + ) + ) + ) + (set_local $2 + (i32.or + (get_local $2) + (get_local $6) + ) + ) + (return + (f32.reinterpret/i32 + (get_local $2) + ) + ) + ) + (func $start (; 2 ;) (type $v) (local $0 f64) + (local $1 f32) (if (i32.eqz (f64.ne @@ -539,6 +990,62 @@ ) (unreachable) ) + (if + (i32.eqz + (f32.ne + (tee_local $1 + (call $fmod/fmodf + (f32.const 1) + (f32.const nan:0x400000) + ) + ) + (get_local $1) + ) + ) + (unreachable) + ) + (if + (i32.eqz + (f32.eq + (call $fmod/fmodf + (f32.const 1.5) + (f32.const 1) + ) + (f32.const 0.5) + ) + ) + (unreachable) + ) + (if + (i32.eqz + (f32.lt + (f32.sub + (call $fmod/fmodf + (f32.const 9.199999809265137) + (f32.const 2) + ) + (f32.const 1.2000000476837158) + ) + (f32.const 1.1920928955078125e-07) + ) + ) + (unreachable) + ) + (if + (i32.eqz + (f32.lt + (f32.sub + (call $fmod/fmodf + (f32.const 9.199999809265137) + (f32.const 3.700000047683716) + ) + (f32.const 1.7999999523162842) + ) + (f32.const 1.1920928955078125e-07) + ) + ) + (unreachable) + ) ) ) (; @@ -588,6 +1095,8 @@ FUNCTION_PROTOTYPE: usize GLOBAL: HEAP_BASE FUNCTION_PROTOTYPE: fmod/fmod + FUNCTION_PROTOTYPE: fmod/fmodf [program.exports] FUNCTION_PROTOTYPE: fmod/fmod + FUNCTION_PROTOTYPE: fmod/fmodf ;) diff --git a/tests/compiler/namespace.optimized-inlined.wast b/tests/compiler/namespace.optimized-inlined.wast index ceffb7ae..99616394 100644 --- a/tests/compiler/namespace.optimized-inlined.wast +++ b/tests/compiler/namespace.optimized-inlined.wast @@ -2,7 +2,6 @@ (type $i (func (result i32))) (type $v (func)) (global $namespace/Outer.Inner.aVar (mut i32) (i32.const 0)) - (global $namespace/Joined.THREE i32 (i32.const 3)) (memory $0 1) (export "memory" (memory $0)) (start $start) @@ -17,7 +16,7 @@ (drop (block (result i32) (block $__inlined_func$namespace/Joined.anotherFunc (result i32) - (get_global $namespace/Joined.THREE) + (i32.const 3) ) ) ) diff --git a/tests/compiler/namespace.optimized.wast b/tests/compiler/namespace.optimized.wast index a2c8c64d..5ee35234 100644 --- a/tests/compiler/namespace.optimized.wast +++ b/tests/compiler/namespace.optimized.wast @@ -2,7 +2,6 @@ (type $i (func (result i32))) (type $v (func)) (global $namespace/Outer.Inner.aVar (mut i32) (i32.const 0)) - (global $namespace/Joined.THREE i32 (i32.const 3)) (memory $0 1) (export "memory" (memory $0)) (start $start) @@ -10,7 +9,7 @@ (get_global $namespace/Outer.Inner.aVar) ) (func $namespace/Joined.anotherFunc (; 1 ;) (type $i) (result i32) - (get_global $namespace/Joined.THREE) + (i32.const 3) ) (func $start (; 2 ;) (type $v) (drop diff --git a/tests/compiler/namespace.wast b/tests/compiler/namespace.wast index 9a2ee3e1..d6ed5f5f 100644 --- a/tests/compiler/namespace.wast +++ b/tests/compiler/namespace.wast @@ -16,7 +16,7 @@ ) (func $namespace/Joined.anotherFunc (; 1 ;) (type $i) (result i32) (return - (get_global $namespace/Joined.THREE) + (i32.const 3) ) ) (func $start (; 2 ;) (type $v) @@ -27,7 +27,7 @@ (call $namespace/Outer.Inner.aFunc) ) (drop - (get_global $namespace/Outer.Inner.anEnum.ONE) + (i32.const 1) ) (drop (call $namespace/Joined.anotherFunc)