mirror of
https://github.com/fluencelabs/assemblyscript
synced 2025-06-29 14:41:52 +00:00
Use long.js in JS and native i64 in WASM; Compile literals more thoroughly
This commit is contained in:
README.md
bin
dist
package-lock.jsonpackage.jsonsrc
std/assembly
tests
compiler.js
webpack.config.jscompiler
assert.optimized.wastassert.wastbuiltins.optimized.wastbuiltins.wastclass.optimized.wastclass.wastcomma.optimized.wastcomma.wastdeclare.optimized.wastdeclare.wastdo.optimized.wastdo.wastfmod.optimized.wastfmod.tsfmod.wastfor.optimized.wastfor.wastgetter-setter.optimized.wastgetter-setter.wastif.optimized.wastif.wastinfer-type.optimized.wastinfer-type.wastinlining.optimized.wastinlining.wastlimits.wastlogical.optimized.wastlogical.wastmemcpy.optimized.wastmemcpy.wastmemmove.optimized.wastmemmove.wastmemset.optimized.wastmemset.tsmemset.wastoverflow.optimized.wastoverflow.wastretain-i32.optimized.wastretain-i32.wastshowcase.optimized.wastshowcase.wast
std
allocator_arena.optimized.wastallocator_arena.wastarray.optimized.wastarray.wastcarray.optimized.wastcarray.wastset.optimized.wastset.waststring.optimized.waststring.wast
tlsf.optimized.wasttlsf.wastunary.wastwhile.optimized.wastwhile.wasttlsf
@ -10,10 +10,6 @@ import {
|
||||
Range
|
||||
} from "./tokenizer";
|
||||
|
||||
import {
|
||||
I64
|
||||
} from "./util/i64";
|
||||
|
||||
import {
|
||||
normalize as normalizePath,
|
||||
resolve as resolvePath
|
||||
|
252
src/compiler.ts
252
src/compiler.ts
@ -43,6 +43,8 @@ import {
|
||||
Flow,
|
||||
FlowFlags,
|
||||
ElementFlags,
|
||||
ConstantValueKind,
|
||||
|
||||
PATH_DELIMITER,
|
||||
LIBRARY_PREFIX
|
||||
} from "./program";
|
||||
@ -120,11 +122,6 @@ import {
|
||||
typesToNativeTypes
|
||||
} from "./types";
|
||||
|
||||
import {
|
||||
I64,
|
||||
U64
|
||||
} from "./util/i64";
|
||||
|
||||
import {
|
||||
sb
|
||||
} from "./util/sb";
|
||||
@ -198,7 +195,7 @@ export class Compiler extends DiagnosticEmitter {
|
||||
currentType: Type = Type.void;
|
||||
|
||||
/** Counting memory offset. */
|
||||
memoryOffset: U64 = new U64(8, 0); // leave space for (any size of) NULL
|
||||
memoryOffset: I64;
|
||||
/** Memory segments being compiled. */
|
||||
memorySegments: MemorySegment[] = new Array();
|
||||
/** Map of already compiled static string segments. */
|
||||
@ -217,7 +214,7 @@ export class Compiler extends DiagnosticEmitter {
|
||||
super(program.diagnostics);
|
||||
this.program = program;
|
||||
this.options = options ? options : new Options();
|
||||
this.memoryOffset = new U64(this.options.usizeType.byteSize); // leave space for `null`
|
||||
this.memoryOffset = i64_new(this.options.usizeType.byteSize, 0); // leave space for `null`
|
||||
this.module = Module.create();
|
||||
}
|
||||
|
||||
@ -255,20 +252,16 @@ export class Compiler extends DiagnosticEmitter {
|
||||
|
||||
// set up static memory segments and the heap base pointer
|
||||
if (!this.options.noMemory) {
|
||||
var initial = this.memoryOffset.clone();
|
||||
var alignMask = this.options.usizeType.byteSize - 1;
|
||||
initial.add32(alignMask); // align to 4/8 bytes
|
||||
initial.and32(~alignMask, ~0);
|
||||
var memoryOffset = this.memoryOffset;
|
||||
this.memoryOffset = memoryOffset = i64_align(memoryOffset, this.options.usizeType.byteSize);
|
||||
if (this.options.target == Target.WASM64)
|
||||
this.module.addGlobal("HEAP_BASE", NativeType.I64, false, this.module.createI64(initial.lo, initial.hi));
|
||||
this.module.addGlobal("HEAP_BASE", NativeType.I64, false, this.module.createI64(i64_low(memoryOffset), i64_high(memoryOffset)));
|
||||
else
|
||||
this.module.addGlobal("HEAP_BASE", NativeType.I32, false, this.module.createI32(initial.lo));
|
||||
this.module.addGlobal("HEAP_BASE", NativeType.I32, false, this.module.createI32(i64_low(memoryOffset)));
|
||||
|
||||
// determine initial page size
|
||||
initial.add32(0xffff); // align to page size
|
||||
initial.and32(~0xffff, ~0);
|
||||
initial.shru32(16); // ^= number of pages
|
||||
this.module.setMemory(initial.toI32(), Module.MAX_MEMORY_WASM32 /* TODO: not WASM64 compatible yet */, this.memorySegments, this.options.target, "memory");
|
||||
var pages = i64_shr_u(i64_align(memoryOffset, 0x10000), i64_new(16, 0));
|
||||
this.module.setMemory(i64_low(pages), Module.MAX_MEMORY_WASM32 /* TODO: not WASM64 compatible yet */, this.memorySegments, this.options.target, "memory");
|
||||
}
|
||||
return this.module;
|
||||
}
|
||||
@ -458,18 +451,22 @@ export class Compiler extends DiagnosticEmitter {
|
||||
switch (exprType) {
|
||||
|
||||
case NativeType.I32:
|
||||
global.constantIntegerValue = new I64(_BinaryenConstGetValueI32(initExpr), 0);
|
||||
global.constantValueKind = ConstantValueKind.INTEGER;
|
||||
global.constantIntegerValue = i64_new(_BinaryenConstGetValueI32(initExpr), 0);
|
||||
break;
|
||||
|
||||
case NativeType.I64:
|
||||
global.constantIntegerValue = new I64(_BinaryenConstGetValueI64Low(initExpr), _BinaryenConstGetValueI64High(initExpr));
|
||||
global.constantValueKind = ConstantValueKind.INTEGER;
|
||||
global.constantIntegerValue = i64_new(_BinaryenConstGetValueI64Low(initExpr), _BinaryenConstGetValueI64High(initExpr));
|
||||
break;
|
||||
|
||||
case NativeType.F32:
|
||||
global.constantValueKind = ConstantValueKind.FLOAT;
|
||||
global.constantFloatValue = _BinaryenConstGetValueF32(initExpr);
|
||||
break;
|
||||
|
||||
case NativeType.F64:
|
||||
global.constantValueKind = ConstantValueKind.FLOAT;
|
||||
global.constantFloatValue = _BinaryenConstGetValueF64(initExpr);
|
||||
break;
|
||||
|
||||
@ -819,14 +816,11 @@ export class Compiler extends DiagnosticEmitter {
|
||||
// memory
|
||||
|
||||
/** Adds a static memory segment with the specified data. */
|
||||
addMemorySegment(buffer: Uint8Array): MemorySegment {
|
||||
if (this.memoryOffset.lo & 7) { // align to 8 bytes so any native data type is aligned here
|
||||
this.memoryOffset.or32(7);
|
||||
this.memoryOffset.add32(1);
|
||||
}
|
||||
var segment = MemorySegment.create(buffer, this.memoryOffset.clone());
|
||||
addMemorySegment(buffer: Uint8Array, alignment: i32 = 8): MemorySegment {
|
||||
var memoryOffset = i64_align(this.memoryOffset, alignment);
|
||||
var segment = MemorySegment.create(buffer, memoryOffset);
|
||||
this.memorySegments.push(segment);
|
||||
this.memoryOffset.add32(buffer.length);
|
||||
this.memoryOffset = i64_add(memoryOffset, i64_new(buffer.length, 0));
|
||||
return segment;
|
||||
}
|
||||
|
||||
@ -1315,7 +1309,7 @@ export class Compiler extends DiagnosticEmitter {
|
||||
compileInlineConstant(element: VariableLikeElement, contextualType: Type): ExpressionRef {
|
||||
assert(element.is(ElementFlags.INLINED));
|
||||
|
||||
switch (element.type.is(TypeFlags.INTEGER) && contextualType.is(TypeFlags.INTEGER) && element.type.size <= contextualType.size
|
||||
switch (element.type.is(TypeFlags.INTEGER) && contextualType.is(TypeFlags.INTEGER) && element.type.size < contextualType.size
|
||||
? (this.currentType = contextualType).kind // essentially precomputes a (sign-)extension
|
||||
: (this.currentType = element.type).kind
|
||||
) {
|
||||
@ -1323,32 +1317,32 @@ export class Compiler extends DiagnosticEmitter {
|
||||
case TypeKind.I8:
|
||||
case TypeKind.I16:
|
||||
var shift = element.type.computeSmallIntegerShift(Type.i32);
|
||||
return this.module.createI32(element.constantIntegerValue ? element.constantIntegerValue.toI32() << shift >> shift : 0);
|
||||
return this.module.createI32(element.constantValueKind == ConstantValueKind.INTEGER ? i64_low(element.constantIntegerValue) << shift >> shift : 0);
|
||||
|
||||
case TypeKind.U8:
|
||||
case TypeKind.U16:
|
||||
case TypeKind.BOOL:
|
||||
var mask = element.type.computeSmallIntegerMask(Type.i32);
|
||||
return this.module.createI32(element.constantIntegerValue ? element.constantIntegerValue.toI32() & mask : 0);
|
||||
return this.module.createI32(element.constantValueKind == ConstantValueKind.INTEGER ? i64_low(element.constantIntegerValue) & mask : 0);
|
||||
|
||||
case TypeKind.I32:
|
||||
case TypeKind.U32:
|
||||
return this.module.createI32(element.constantIntegerValue ? element.constantIntegerValue.lo : 0)
|
||||
return this.module.createI32(element.constantValueKind == ConstantValueKind.INTEGER ? i64_low(element.constantIntegerValue) : 0)
|
||||
|
||||
case TypeKind.ISIZE:
|
||||
case TypeKind.USIZE:
|
||||
if (!element.program.options.isWasm64)
|
||||
return this.module.createI32(element.constantIntegerValue ? element.constantIntegerValue.lo : 0)
|
||||
return this.module.createI32(element.constantValueKind == ConstantValueKind.INTEGER ? i64_low(element.constantIntegerValue) : 0)
|
||||
// fall-through
|
||||
|
||||
case TypeKind.I64:
|
||||
case TypeKind.U64:
|
||||
return element.constantIntegerValue
|
||||
? this.module.createI64(element.constantIntegerValue.lo, element.constantIntegerValue.hi)
|
||||
return element.constantValueKind == ConstantValueKind.INTEGER
|
||||
? this.module.createI64(i64_low(element.constantIntegerValue), i64_high(element.constantIntegerValue))
|
||||
: this.module.createI64(0);
|
||||
|
||||
case TypeKind.F32:
|
||||
return this.module.createF32((<VariableLikeElement>element).constantFloatValue);
|
||||
return this.module.createF32((<VariableLikeElement>element).constantFloatValue); // safe because it's a 'number' in JS
|
||||
|
||||
case TypeKind.F64:
|
||||
return this.module.createF64((<VariableLikeElement>element).constantFloatValue);
|
||||
@ -2889,10 +2883,11 @@ export class Compiler extends DiagnosticEmitter {
|
||||
return this.module.createUnreachable();
|
||||
}
|
||||
|
||||
compileLiteralExpression(expression: LiteralExpression, contextualType: Type): ExpressionRef {
|
||||
compileLiteralExpression(expression: LiteralExpression, contextualType: Type, implicitNegate: bool = false): ExpressionRef {
|
||||
switch (expression.literalKind) {
|
||||
|
||||
case LiteralKind.ARRAY:
|
||||
assert(!implicitNegate);
|
||||
var classType = contextualType.classType;
|
||||
if (classType && classType == this.program.elements.get("Array") && classType.typeArguments && classType.typeArguments.length == 1)
|
||||
return this.compileStaticArray(classType.typeArguments[0], (<ArrayLiteralExpression>expression).elementExpressions);
|
||||
@ -2901,6 +2896,8 @@ export class Compiler extends DiagnosticEmitter {
|
||||
|
||||
case LiteralKind.FLOAT: {
|
||||
var floatValue = (<FloatLiteralExpression>expression).value;
|
||||
if (implicitNegate)
|
||||
floatValue = -floatValue;
|
||||
if (contextualType == Type.f32)
|
||||
return this.module.createF32(<f32>floatValue);
|
||||
this.currentType = Type.f64;
|
||||
@ -2909,31 +2906,97 @@ export class Compiler extends DiagnosticEmitter {
|
||||
|
||||
case LiteralKind.INTEGER:
|
||||
var intValue = (<IntegerLiteralExpression>expression).value;
|
||||
if (contextualType == Type.bool && (intValue.isZero || intValue.isOne))
|
||||
return this.module.createI32(intValue.isZero ? 0 : 1);
|
||||
if (contextualType == Type.f64)
|
||||
return this.module.createF64(intValue.toF64());
|
||||
if (contextualType == Type.f32)
|
||||
return this.module.createF32(<f32>intValue.toF64());
|
||||
if (contextualType.is(TypeFlags.LONG | TypeFlags.INTEGER))
|
||||
return this.module.createI64(intValue.lo, intValue.hi);
|
||||
if (!intValue.fitsInI32) {
|
||||
this.currentType = contextualType.is(TypeFlags.SIGNED) ? Type.i64 : Type.u64;
|
||||
return this.module.createI64(intValue.lo, intValue.hi);
|
||||
if (implicitNegate)
|
||||
intValue = i64_sub(i64_new(0), intValue);
|
||||
switch (contextualType.kind) {
|
||||
|
||||
// compile to contextualType if matching
|
||||
|
||||
case TypeKind.I8:
|
||||
if (i64_is_i8(intValue))
|
||||
return this.module.createI32(i64_low(intValue));
|
||||
break;
|
||||
|
||||
case TypeKind.I16:
|
||||
if (i64_is_i16(intValue))
|
||||
return this.module.createI32(i64_low(intValue));
|
||||
break;
|
||||
|
||||
case TypeKind.I32:
|
||||
if (i64_is_i32(intValue))
|
||||
return this.module.createI32(i64_low(intValue));
|
||||
break;
|
||||
|
||||
case TypeKind.U8:
|
||||
if (i64_is_u8(intValue))
|
||||
return this.module.createI32(i64_low(intValue));
|
||||
break;
|
||||
|
||||
case TypeKind.U16:
|
||||
if (i64_is_u16(intValue))
|
||||
return this.module.createI32(i64_low(intValue));
|
||||
break;
|
||||
|
||||
case TypeKind.U32:
|
||||
if (i64_is_u32(intValue))
|
||||
return this.module.createI32(i64_low(intValue));
|
||||
break;
|
||||
|
||||
case TypeKind.BOOL:
|
||||
if (i64_is_bool(intValue))
|
||||
return this.module.createI32(i64_low(intValue));
|
||||
break;
|
||||
|
||||
case TypeKind.ISIZE:
|
||||
if (!this.options.isWasm64) {
|
||||
if (i64_is_u32(intValue))
|
||||
return this.module.createI32(i64_low(intValue));
|
||||
break;
|
||||
}
|
||||
return this.module.createI64(i64_low(intValue), i64_high(intValue));
|
||||
|
||||
case TypeKind.USIZE:
|
||||
if (!this.options.isWasm64) {
|
||||
if (i64_is_u32(intValue))
|
||||
return this.module.createI32(i64_low(intValue));
|
||||
break;
|
||||
}
|
||||
return this.module.createI64(i64_low(intValue), i64_high(intValue));
|
||||
|
||||
case TypeKind.I64:
|
||||
case TypeKind.U64:
|
||||
return this.module.createI64(i64_low(intValue), i64_high(intValue));
|
||||
|
||||
case TypeKind.F32:
|
||||
if (i64_is_f32(intValue))
|
||||
return this.module.createF32(i64_to_f32(intValue));
|
||||
break;
|
||||
|
||||
case TypeKind.F64:
|
||||
if (i64_is_f64(intValue))
|
||||
return this.module.createF64(i64_to_f64(intValue));
|
||||
break;
|
||||
|
||||
case TypeKind.VOID:
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
if (contextualType.is(TypeFlags.SMALL | TypeFlags.INTEGER)) {
|
||||
var shift = contextualType.computeSmallIntegerShift(Type.i32);
|
||||
var mask = contextualType.computeSmallIntegerMask(Type.i32);
|
||||
return this.module.createI32(contextualType.is(TypeFlags.SIGNED) ? intValue.lo << shift >> shift : intValue.lo & mask);
|
||||
}
|
||||
if (contextualType == Type.void && !intValue.fitsInI32) {
|
||||
|
||||
// otherwise compile to best fitting native type
|
||||
|
||||
if (i64_is_i32(intValue)) {
|
||||
this.currentType = Type.i32;
|
||||
return this.module.createI32(i64_low(intValue));
|
||||
} else {
|
||||
this.currentType = Type.i64;
|
||||
return this.module.createI64(intValue.lo, intValue.hi);
|
||||
return this.module.createI64(i64_low(intValue), i64_high(intValue));
|
||||
}
|
||||
this.currentType = contextualType.is(TypeFlags.SIGNED) ? Type.i32 : Type.u32;
|
||||
return this.module.createI32(intValue.toI32());
|
||||
|
||||
case LiteralKind.STRING:
|
||||
assert(!implicitNegate);
|
||||
return this.compileStaticString((<StringLiteralExpression>expression).value);
|
||||
|
||||
// case LiteralKind.OBJECT:
|
||||
@ -2955,14 +3018,15 @@ export class Compiler extends DiagnosticEmitter {
|
||||
stringBuffer[4 + i * 2] = stringValue.charCodeAt(i) & 0xff;
|
||||
stringBuffer[5 + i * 2] = (stringValue.charCodeAt(i) >>> 8) & 0xff;
|
||||
}
|
||||
stringSegment = this.addMemorySegment(stringBuffer);
|
||||
stringSegment = this.addMemorySegment(stringBuffer, this.options.usizeType.byteSize);
|
||||
this.stringSegments.set(stringValue, stringSegment);
|
||||
}
|
||||
var stringOffset = stringSegment.offset;
|
||||
this.currentType = this.options.usizeType;
|
||||
return this.options.isWasm64
|
||||
? this.module.createI64(stringOffset.lo, stringOffset.hi)
|
||||
: this.module.createI32(stringOffset.lo);
|
||||
if (this.options.isWasm64)
|
||||
return this.module.createI64(i64_low(stringOffset), i64_high(stringOffset));
|
||||
assert(i64_is_i32(stringOffset));
|
||||
return this.module.createI32(i64_low(stringOffset));
|
||||
}
|
||||
|
||||
compileStaticArray(elementType: Type, expressions: (Expression | null)[]): ExpressionRef {
|
||||
@ -3009,7 +3073,7 @@ export class Compiler extends DiagnosticEmitter {
|
||||
break;
|
||||
|
||||
case NativeType.I64:
|
||||
changetype<I64[]>(values)[i] = new I64(_BinaryenConstGetValueI64Low(expr), _BinaryenConstGetValueI64High(expr));
|
||||
changetype<I64[]>(values)[i] = i64_new(_BinaryenConstGetValueI64Low(expr), _BinaryenConstGetValueI64High(expr));
|
||||
break;
|
||||
|
||||
case NativeType.F32:
|
||||
@ -3325,41 +3389,49 @@ export class Compiler extends DiagnosticEmitter {
|
||||
break;
|
||||
|
||||
case Token.MINUS:
|
||||
expr = this.compileExpression(expression.operand, contextualType == Type.void ? Type.i32 : contextualType, ConversionKind.NONE, false);
|
||||
if (expression.operand.kind == NodeKind.LITERAL && (
|
||||
(<LiteralExpression>expression.operand).literalKind == LiteralKind.INTEGER ||
|
||||
(<LiteralExpression>expression.operand).literalKind == LiteralKind.FLOAT
|
||||
)) {
|
||||
// implicitly negate integer and float literals. also enables proper checking of literal ranges.
|
||||
expr = this.compileLiteralExpression(<LiteralExpression>expression.operand, contextualType, true);
|
||||
this.addDebugLocation(expr, expression.range); // compileExpression normally does this
|
||||
} else {
|
||||
expr = this.compileExpression(expression.operand, contextualType == Type.void ? Type.i32 : contextualType, ConversionKind.NONE, false);
|
||||
switch (this.currentType.kind) {
|
||||
|
||||
switch (this.currentType.kind) {
|
||||
case TypeKind.I8:
|
||||
case TypeKind.I16:
|
||||
case TypeKind.U8:
|
||||
case TypeKind.U16:
|
||||
case TypeKind.BOOL:
|
||||
possiblyOverflows = true; // or if operand already did
|
||||
default:
|
||||
expr = this.module.createBinary(BinaryOp.SubI32, this.module.createI32(0), expr);
|
||||
break;
|
||||
|
||||
case TypeKind.I8:
|
||||
case TypeKind.I16:
|
||||
case TypeKind.U8:
|
||||
case TypeKind.U16:
|
||||
case TypeKind.BOOL:
|
||||
possiblyOverflows = true; // or if operand already did
|
||||
default:
|
||||
expr = this.module.createBinary(BinaryOp.SubI32, this.module.createI32(0), expr);
|
||||
break;
|
||||
case TypeKind.USIZE:
|
||||
if (this.currentType.isReference) {
|
||||
this.error(DiagnosticCode.Operation_not_supported, expression.range);
|
||||
return this.module.createUnreachable();
|
||||
}
|
||||
case TypeKind.ISIZE:
|
||||
expr = this.module.createBinary(this.options.target == Target.WASM64 ? BinaryOp.SubI64 : BinaryOp.SubI32, this.currentType.toNativeZero(this.module), expr);
|
||||
break;
|
||||
|
||||
case TypeKind.USIZE:
|
||||
if (this.currentType.isReference) {
|
||||
this.error(DiagnosticCode.Operation_not_supported, expression.range);
|
||||
return this.module.createUnreachable();
|
||||
}
|
||||
case TypeKind.ISIZE:
|
||||
expr = this.module.createBinary(this.options.target == Target.WASM64 ? BinaryOp.SubI64 : BinaryOp.SubI32, this.currentType.toNativeZero(this.module), expr);
|
||||
break;
|
||||
case TypeKind.I64:
|
||||
case TypeKind.U64:
|
||||
expr = this.module.createBinary(BinaryOp.SubI64, this.module.createI64(0), expr);
|
||||
break;
|
||||
|
||||
case TypeKind.I64:
|
||||
case TypeKind.U64:
|
||||
expr = this.module.createBinary(BinaryOp.SubI64, this.module.createI64(0), expr);
|
||||
break;
|
||||
case TypeKind.F32:
|
||||
expr = this.module.createUnary(UnaryOp.NegF32, expr);
|
||||
break;
|
||||
|
||||
case TypeKind.F32:
|
||||
expr = this.module.createUnary(UnaryOp.NegF32, expr);
|
||||
break;
|
||||
|
||||
case TypeKind.F64:
|
||||
expr = this.module.createUnary(UnaryOp.NegF64, expr);
|
||||
break;
|
||||
case TypeKind.F64:
|
||||
expr = this.module.createUnary(UnaryOp.NegF64, expr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -11,10 +11,6 @@ import {
|
||||
readString
|
||||
} from "./module";
|
||||
|
||||
import {
|
||||
I64
|
||||
} from "./util/i64";
|
||||
|
||||
// TODO :-)
|
||||
|
||||
export class Decompiler {
|
||||
@ -28,8 +24,6 @@ export class Decompiler {
|
||||
text: string[] = [];
|
||||
functionId: i32 = 0;
|
||||
|
||||
private tempI64: I64 = new I64();
|
||||
|
||||
constructor() { }
|
||||
|
||||
/** Decompiles a module to an AST that can then be serialized. */
|
||||
@ -183,9 +177,14 @@ export class Decompiler {
|
||||
return;
|
||||
|
||||
case NativeType.I64:
|
||||
this.tempI64.lo = _BinaryenConstGetValueI64Low(expr);
|
||||
this.tempI64.hi = _BinaryenConstGetValueI64High(expr);
|
||||
this.push(this.tempI64.toString());
|
||||
this.push(
|
||||
i64_to_string(
|
||||
i64_new(
|
||||
_BinaryenConstGetValueI64Low(expr),
|
||||
_BinaryenConstGetValueI64High(expr)
|
||||
)
|
||||
)
|
||||
);
|
||||
return;
|
||||
|
||||
case NativeType.F32:
|
||||
|
6
src/extra/tsconfig.json
Normal file
6
src/extra/tsconfig.json
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"extends": "../../std/portable.json",
|
||||
"include": [
|
||||
"./**/*.ts"
|
||||
]
|
||||
}
|
@ -1,54 +0,0 @@
|
||||
require("../../std/portable");
|
||||
|
||||
// Copy Binaryen exports to global scope
|
||||
var globalScope = typeof window !== "undefined" && window || typeof global !== "undefined" && global || self;
|
||||
var binaryen = globalScope["Binaryen"]; // allow overriding for testing purposes
|
||||
if (!binaryen) {
|
||||
try {
|
||||
binaryen = require("binaryen");
|
||||
} catch (e) {
|
||||
binaryen = globalScope["Binaryen"];
|
||||
}
|
||||
}
|
||||
for (var key in binaryen)
|
||||
if (/^_(?:Binaryen|Relooper)/.test(key))
|
||||
globalScope[key] = binaryen[key];
|
||||
|
||||
// Use Binaryen's heap instead of std heap
|
||||
globalScope["allocate_memory"] = function allocate_memory(size) {
|
||||
if (!size) return 0; // should be safe in our case
|
||||
return binaryen._malloc(size);
|
||||
};
|
||||
globalScope["free_memory"] = function free_memory(ptr) {
|
||||
if (ptr) binaryen._free(ptr);
|
||||
};
|
||||
globalScope["move_memory"] = function move_memory(dest, src, n) {
|
||||
return binaryen._memmove(dest, src, n);
|
||||
};
|
||||
globalScope["store"] = function store(ptr, val) {
|
||||
binaryen.HEAPU8[ptr] = val;
|
||||
};
|
||||
globalScope["load"] = function load(ptr) {
|
||||
return binaryen.HEAPU8[ptr];
|
||||
};
|
||||
|
||||
// Implement module stubs
|
||||
var Module = require("../module").Module;
|
||||
|
||||
Module.prototype.toText = function toText() {
|
||||
var previousPrint = binaryen.print;
|
||||
var ret = "";
|
||||
binaryen.print = function print(x) { ret += x + "\n" };
|
||||
this.print();
|
||||
binaryen.print = previousPrint;
|
||||
return ret;
|
||||
};
|
||||
|
||||
Module.prototype.toAsmjs = function toAsmjs() {
|
||||
var previousPrint = binaryen.print;
|
||||
var ret = "";
|
||||
binaryen.print = function print(x) { ret += x + "\n" };
|
||||
this.printAsmjs();
|
||||
binaryen.print = previousPrint;
|
||||
return ret;
|
||||
};
|
36
src/glue/js/i64.d.ts
vendored
Normal file
36
src/glue/js/i64.d.ts
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
declare type I64 = Long;
|
||||
|
||||
declare function i64_new(lo: i32, hi?: i32): I64;
|
||||
declare function i64_low(value: I64): i32;
|
||||
declare function i64_high(value: I64): i32;
|
||||
|
||||
declare function i64_add(left: I64, right: I64): I64;
|
||||
declare function i64_sub(left: I64, right: I64): I64;
|
||||
declare function i64_mul(left: I64, right: I64): I64;
|
||||
declare function i64_div(left: I64, right: I64): I64;
|
||||
declare function i64_div_u(left: I64, right: I64): I64;
|
||||
declare function i64_rem(left: I64, right: I64): I64;
|
||||
declare function i64_rem_u(left: I64, right: I64): I64;
|
||||
declare function i64_and(left: I64, right: I64): I64;
|
||||
declare function i64_or(left: I64, right: I64): I64;
|
||||
declare function i64_xor(left: I64, right: I64): I64;
|
||||
declare function i64_shl(left: I64, right: I64): I64;
|
||||
declare function i64_shr(left: I64, right: I64): I64;
|
||||
declare function i64_shr_u(left: I64, right: I64): I64;
|
||||
declare function i64_not(value: I64): I64;
|
||||
|
||||
declare function i64_align(value: I64, alignment: i32): I64;
|
||||
|
||||
declare function i64_is_i8(value: I64): bool;
|
||||
declare function i64_is_i16(value: I64): bool;
|
||||
declare function i64_is_i32(value: I64): bool;
|
||||
declare function i64_is_u8(value: I64): bool;
|
||||
declare function i64_is_u16(value: I64): bool;
|
||||
declare function i64_is_u32(value: I64): bool;
|
||||
declare function i64_is_bool(value: I64): bool;
|
||||
declare function i64_is_f32(value: I64): bool;
|
||||
declare function i64_is_f64(value: I64): bool;
|
||||
|
||||
declare function i64_to_f32(value: I64): f64;
|
||||
declare function i64_to_f64(value: I64): f64;
|
||||
declare function i64_to_string(value: I64, unsigned?: bool): string;
|
193
src/glue/js/index.ts
Normal file
193
src/glue/js/index.ts
Normal file
@ -0,0 +1,193 @@
|
||||
import "../../../std/portable";
|
||||
|
||||
// Copy Binaryen exports to global scope
|
||||
|
||||
declare const global: any;
|
||||
declare function require(name: string): any;
|
||||
|
||||
const binaryen: any = global.Binaryen || require("binaryen");
|
||||
|
||||
for (let key in binaryen)
|
||||
if (key.startsWith("_Binaryen") || key.startsWith("_Relooper"))
|
||||
global[key] = (<any>binaryen)[key];
|
||||
|
||||
// Use Binaryen's heap instead of std heap
|
||||
|
||||
global.allocate_memory = function(size: number): number {
|
||||
if (!size) return 0; // should be safe in our case
|
||||
return (<any>binaryen)._malloc(size);
|
||||
};
|
||||
|
||||
global.free_memory = function(ptr: number): void {
|
||||
if (ptr) (<any>binaryen)._free(ptr);
|
||||
};
|
||||
|
||||
global.move_memory = function(dest: number, src: number, n: number): number {
|
||||
return (<any>binaryen)._memmove(dest, src, n);
|
||||
};
|
||||
|
||||
global.store = function(ptr: number, val: number): void {
|
||||
(<any>binaryen).HEAPU8[ptr] = val;
|
||||
};
|
||||
|
||||
global.load = function(ptr: number): number {
|
||||
return (<any>binaryen).HEAPU8[ptr];
|
||||
};
|
||||
|
||||
// Implement module stubs
|
||||
|
||||
import { Module } from "../../module";
|
||||
|
||||
Module.prototype.toText = function toText() {
|
||||
var previousPrint = binaryen.print;
|
||||
var ret = "";
|
||||
binaryen.print = (x: string) => { ret += x + "\n" };
|
||||
this.print();
|
||||
binaryen.print = previousPrint;
|
||||
return ret;
|
||||
};
|
||||
|
||||
Module.prototype.toAsmjs = function toAsmjs() {
|
||||
var previousPrint = binaryen.print;
|
||||
var ret = "";
|
||||
binaryen.print = (x: string) => { ret += x + "\n" };
|
||||
this.printAsmjs();
|
||||
binaryen.print = previousPrint;
|
||||
return ret;
|
||||
};
|
||||
|
||||
// Implement I64 using long.js
|
||||
|
||||
import * as Long from "long";
|
||||
|
||||
/// <reference path="./i64.d.ts" />
|
||||
|
||||
global.i64_new = function(lo: number, hi: number = 0): I64 {
|
||||
return Long.fromBits(lo, hi);
|
||||
};
|
||||
|
||||
global.i64_low = function(value: I64): i32 {
|
||||
return value.low;
|
||||
};
|
||||
|
||||
global.i64_high = function(value: I64): i32 {
|
||||
return value.high;
|
||||
};
|
||||
|
||||
global.i64_add = function(left: I64, right: I64): I64 {
|
||||
return left.add(right);
|
||||
};
|
||||
|
||||
global.i64_sub = function(left: I64, right: I64): I64 {
|
||||
return left.sub(right);
|
||||
};
|
||||
|
||||
global.i64_mul = function(left: I64, right: I64): I64 {
|
||||
return left.mul(right);
|
||||
};
|
||||
|
||||
global.i64_div = function(left: I64, right: I64): I64 {
|
||||
return left.div(right);
|
||||
};
|
||||
|
||||
global.i64_div_u = function(left: I64, right: I64): I64 {
|
||||
return left.toUnsigned().div(right.toUnsigned()).toSigned();
|
||||
};
|
||||
|
||||
global.i64_rem = function(left: I64, right: I64): I64 {
|
||||
return left.mod(right);
|
||||
};
|
||||
|
||||
global.i64_rem_u = function(left: I64, right: I64): I64 {
|
||||
return left.toUnsigned().mod(right.toUnsigned()).toSigned();
|
||||
};
|
||||
|
||||
global.i64_and = function(left: I64, right: I64): I64 {
|
||||
return left.and(right);
|
||||
};
|
||||
|
||||
global.i64_or = function(left: I64, right: I64): I64 {
|
||||
return left.or(right);
|
||||
};
|
||||
|
||||
global.i64_xor = function(left: I64, right: I64): I64 {
|
||||
return left.xor(right);
|
||||
};
|
||||
|
||||
global.i64_shl = function(left: I64, right: I64): I64 {
|
||||
return left.shl(right);
|
||||
};
|
||||
|
||||
global.i64_shr = function(left: I64, right: I64): I64 {
|
||||
return left.shr(right);
|
||||
};
|
||||
|
||||
global.i64_shr_u = function(left: I64, right: I64): I64 {
|
||||
return left.shru(right);
|
||||
};
|
||||
|
||||
global.i64_not = function(value: I64): I64 {
|
||||
return value.not();
|
||||
};
|
||||
|
||||
global.i64_align = function(value: I64, alignment: i32): I64 {
|
||||
assert(alignment && (alignment & (alignment - 1)) == 0);
|
||||
var mask = Long.fromInt(alignment - 1);
|
||||
return value.add(mask).and(mask.not());
|
||||
};
|
||||
|
||||
global.i64_is_i8 = function(value: I64): bool {
|
||||
return value.high === 0 && (value.low >= 0 && value.low <= i8.MAX_VALUE)
|
||||
|| value.high === -1 && (value.low >= i8.MIN_VALUE && value.low < 0);
|
||||
};
|
||||
|
||||
global.i64_is_i16 = function(value: I64): bool {
|
||||
return value.high === 0 && (value.low >= 0 && value.low <= i16.MAX_VALUE)
|
||||
|| value.high === -1 && (value.low >= i16.MIN_VALUE && value.low < 0);
|
||||
};
|
||||
|
||||
global.i64_is_i32 = function(value: I64): bool {
|
||||
return (value.high === 0 && value.low >= 0) || (value.high === -1 && value.low < 0);
|
||||
};
|
||||
|
||||
global.i64_is_u8 = function(value: I64): bool {
|
||||
return value.high === 0 && value.low >= 0 && value.low <= u8.MAX_VALUE;
|
||||
};
|
||||
|
||||
global.i64_is_u16 = function(value: I64): bool {
|
||||
return value.high === 0 && value.low >= 0 && value.low <= u16.MAX_VALUE;
|
||||
};
|
||||
|
||||
global.i64_is_u32 = function(value: I64): bool {
|
||||
return value.high === 0;
|
||||
};
|
||||
|
||||
global.i64_is_bool = function(value: I64): bool {
|
||||
return value.high === 0 && (value.low === 0 || value.low === 1);
|
||||
};
|
||||
|
||||
const minSafeF32 = Long.fromNumber(f32.MIN_SAFE_INTEGER);
|
||||
const maxSafeF32 = Long.fromNumber(f32.MAX_SAFE_INTEGER);
|
||||
|
||||
global.i64_is_f32 = function(value: I64): bool {
|
||||
return value.gte(minSafeF32) && value.lte(maxSafeF32);
|
||||
};
|
||||
|
||||
const minSafeF64 = Long.fromNumber(f64.MIN_SAFE_INTEGER);
|
||||
const maxSafeF64 = Long.fromNumber(f64.MAX_SAFE_INTEGER);
|
||||
|
||||
global.i64_is_f64 = function(value: I64): bool {
|
||||
return value.gte(minSafeF64) && value.lte(maxSafeF64);
|
||||
};
|
||||
|
||||
global.i64_to_f32 = function(value: I64): f64 {
|
||||
return global.Math.fround(value.toNumber());
|
||||
};
|
||||
|
||||
global.i64_to_f64 = function(value: I64): f64 {
|
||||
return value.toNumber();
|
||||
};
|
||||
|
||||
global.i64_to_string = function(value: I64, unsigned: bool = false): string {
|
||||
return (unsigned ? value.toUnsigned() : value).toString(10);
|
||||
};
|
164
src/glue/wasm/index.ts
Normal file
164
src/glue/wasm/index.ts
Normal file
@ -0,0 +1,164 @@
|
||||
type I64 = i64;
|
||||
|
||||
@global
|
||||
function i64_new(lo: i32, hi: i32 = 0): I64 {
|
||||
return lo | (hi << 32);
|
||||
}
|
||||
|
||||
@global
|
||||
function i64_low(value: I64): i32 {
|
||||
return <i32>value;
|
||||
}
|
||||
|
||||
@global
|
||||
function i64_high(value: I64): i32 {
|
||||
return <i32>(value >>> 32);
|
||||
}
|
||||
|
||||
@global
|
||||
function i64_add(left: I64, right: I64): I64 {
|
||||
return left + right;
|
||||
}
|
||||
|
||||
@global
|
||||
function i64_sub(left: I64, right: I64): I64 {
|
||||
return left - right;
|
||||
}
|
||||
|
||||
@global
|
||||
function i64_mul(left: I64, right: I64): I64 {
|
||||
return left * right;
|
||||
}
|
||||
|
||||
@global
|
||||
function i64_div(left: I64, right: I64): I64 {
|
||||
return left / right;
|
||||
}
|
||||
|
||||
@global
|
||||
function i64_div_u(left: I64, right: I64): I64 {
|
||||
return <u64>left / <u64>right;
|
||||
}
|
||||
|
||||
@global
|
||||
function i64_rem(left: I64, right: I64): I64 {
|
||||
return left % right;
|
||||
}
|
||||
|
||||
@global
|
||||
function i64_rem_u(left: I64, right: I64): I64 {
|
||||
return <u64>left % <u64>right;
|
||||
}
|
||||
|
||||
@global
|
||||
function i64_and(left: I64, right: I64): I64 {
|
||||
return left & right;
|
||||
}
|
||||
|
||||
@global
|
||||
function i64_or(left: I64, right: I64): I64 {
|
||||
return left | right;
|
||||
}
|
||||
|
||||
@global
|
||||
function i64_xor(left: I64, right: I64): I64 {
|
||||
return left ^ right;
|
||||
}
|
||||
|
||||
@global
|
||||
function i64_shl(left: I64, right: I64): I64 {
|
||||
return left << right;
|
||||
}
|
||||
|
||||
@global
|
||||
function i64_shr(left: I64, right: I64): I64 {
|
||||
return left >> right;
|
||||
}
|
||||
|
||||
@global
|
||||
function i64_shr_u(left: I64, right: I64): I64 {
|
||||
return left >>> right;
|
||||
}
|
||||
|
||||
@global
|
||||
function i64_not(value: I64): I64 {
|
||||
return ~value;
|
||||
}
|
||||
|
||||
@global
|
||||
function i64_align(value: I64, alignment: i64): I64 {
|
||||
var mask: i64 = alignment - 1;
|
||||
assert(alignment && (alignment & mask) == 0);
|
||||
return (value + mask) & ~mask;
|
||||
}
|
||||
|
||||
@global
|
||||
function i64_is_i8(value: I64): bool {
|
||||
return value >= i8.MIN_VALUE && value <= i8.MAX_VALUE;
|
||||
}
|
||||
|
||||
@global
|
||||
function i64_is_i16(value: I64): bool {
|
||||
return value >= i16.MIN_VALUE && value <= i16.MAX_VALUE;
|
||||
}
|
||||
|
||||
@global
|
||||
function i64_is_i32(value: I64): bool {
|
||||
return value >= i32.MIN_VALUE && value <= i32.MAX_VALUE;
|
||||
}
|
||||
|
||||
@global
|
||||
function i64_is_u8(value: I64): bool {
|
||||
return value >= 0 && value <= u8.MAX_VALUE;
|
||||
}
|
||||
|
||||
@global
|
||||
function i64_is_u16(value: I64): bool {
|
||||
return value >= 0 && value <= u16.MAX_VALUE;
|
||||
}
|
||||
|
||||
@global
|
||||
function i64_is_u32(value: I64): bool {
|
||||
return value >= 0 && value <= u32.MAX_VALUE;
|
||||
}
|
||||
|
||||
@global
|
||||
function i64_is_bool(value: I64): bool {
|
||||
return value === 0 || value === 1;
|
||||
}
|
||||
|
||||
@global
|
||||
function i64_is_f32(value: I64): bool {
|
||||
return value >= f32.MIN_SAFE_INTEGER && value <= f32.MAX_SAFE_INTEGER;
|
||||
}
|
||||
|
||||
@global
|
||||
function i64_is_f64(value: I64): bool {
|
||||
return value >= f64.MIN_SAFE_INTEGER && value <= f64.MAX_SAFE_INTEGER;
|
||||
}
|
||||
|
||||
@global
|
||||
function i64_to_f32(value: I64): f32 {
|
||||
return <f32>value;
|
||||
}
|
||||
|
||||
@global
|
||||
function i64_to_f64(value: I64): f64 {
|
||||
return <f64>value;
|
||||
}
|
||||
|
||||
import { CharCode } from "../../util/charcode";
|
||||
|
||||
@global
|
||||
function i64_to_string(value: I64): string {
|
||||
var chars = new Array<u16>();
|
||||
if (value < 0) {
|
||||
chars.push(CharCode.MINUS);
|
||||
value = -value;
|
||||
}
|
||||
do {
|
||||
chars.push(CharCode._0 + (value % 10));
|
||||
value /= 10;
|
||||
} while (value);
|
||||
return String.fromCharCodes(chars);
|
||||
}
|
6
src/glue/wasm/tsconfig.json
Normal file
6
src/glue/wasm/tsconfig.json
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"extends": "../../../std/assembly.json",
|
||||
"include": [
|
||||
"./**/*.ts"
|
||||
]
|
||||
}
|
@ -2,10 +2,6 @@ import {
|
||||
Target
|
||||
} from "./compiler";
|
||||
|
||||
import {
|
||||
U64
|
||||
} from "./util/i64";
|
||||
|
||||
export type ModuleRef = usize;
|
||||
export type FunctionTypeRef = usize;
|
||||
export type FunctionRef = usize;
|
||||
@ -225,9 +221,9 @@ export enum AtomicRMWOp {
|
||||
export class MemorySegment {
|
||||
|
||||
buffer: Uint8Array;
|
||||
offset: U64;
|
||||
offset: I64;
|
||||
|
||||
static create(buffer: Uint8Array, offset: U64) {
|
||||
static create(buffer: Uint8Array, offset: I64) {
|
||||
var segment = new MemorySegment();
|
||||
segment.buffer = buffer;
|
||||
segment.offset = offset;
|
||||
@ -641,8 +637,8 @@ export class Module {
|
||||
var offset = segments[i].offset;
|
||||
segs[i] = allocU8Array(buffer);
|
||||
offs[i] = target == Target.WASM64
|
||||
? this.createI64(offset.lo, offset.hi)
|
||||
: this.createI32(offset.toI32());
|
||||
? this.createI64(i64_low(offset), i64_high(offset))
|
||||
: this.createI32(i64_low(offset));
|
||||
sizs[i] = buffer.length;
|
||||
}
|
||||
var cArr1 = allocI32Array(segs);
|
||||
@ -747,8 +743,11 @@ export class Module {
|
||||
}
|
||||
|
||||
toText(): string {
|
||||
// FIXME: target specific / JS glue overrides this
|
||||
throw new Error("not implemented");
|
||||
throw new Error("not implemented"); // JS glue overrides this
|
||||
}
|
||||
|
||||
toAsmjs(): string {
|
||||
throw new Error("not implemented"); // JS glue overrides this
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
|
@ -23,10 +23,6 @@ import {
|
||||
DiagnosticEmitter
|
||||
} from "./diagnostics";
|
||||
|
||||
import {
|
||||
I64
|
||||
} from "./util/i64";
|
||||
|
||||
import {
|
||||
normalize as normalizePath
|
||||
} from "./util/path";
|
||||
|
@ -13,10 +13,6 @@ import {
|
||||
typesToString
|
||||
} from "./types";
|
||||
|
||||
import {
|
||||
I64
|
||||
} from "./util/i64";
|
||||
|
||||
import {
|
||||
ModifierKind,
|
||||
Node,
|
||||
@ -1446,6 +1442,12 @@ export class EnumValue extends Element {
|
||||
}
|
||||
}
|
||||
|
||||
export const enum ConstantValueKind {
|
||||
NONE,
|
||||
INTEGER,
|
||||
FLOAT
|
||||
}
|
||||
|
||||
export class VariableLikeElement extends Element {
|
||||
|
||||
// kind varies
|
||||
@ -1454,18 +1456,22 @@ export class VariableLikeElement extends Element {
|
||||
declaration: VariableLikeDeclarationStatement;
|
||||
/** Variable type. Is {@link Type.void} for type-inferred {@link Global}s before compilation. */
|
||||
type: Type;
|
||||
/** Constant value kind. */
|
||||
constantValueKind: ConstantValueKind = ConstantValueKind.NONE;
|
||||
/** Constant integer value, if applicable. */
|
||||
constantIntegerValue: I64 | null = null;
|
||||
constantIntegerValue: I64;
|
||||
/** Constant float value, if applicable. */
|
||||
constantFloatValue: f64 = 0;
|
||||
constantFloatValue: f64;
|
||||
|
||||
withConstantIntegerValue(lo: i32, hi: i32): this {
|
||||
this.constantIntegerValue = new I64(lo, hi);
|
||||
this.constantValueKind = ConstantValueKind.INTEGER;
|
||||
this.constantIntegerValue = i64_new(lo, hi);
|
||||
this.set(ElementFlags.CONSTANT | ElementFlags.INLINED);
|
||||
return this;
|
||||
}
|
||||
|
||||
withConstantFloatValue(value: f64): this {
|
||||
this.constantValueKind = ConstantValueKind.FLOAT;
|
||||
this.constantFloatValue = value;
|
||||
this.set(ElementFlags.CONSTANT | ElementFlags.INLINED);
|
||||
return this;
|
||||
|
@ -39,10 +39,6 @@ import {
|
||||
isKeywordCharacter
|
||||
} from "./util/charcode";
|
||||
|
||||
import {
|
||||
I64
|
||||
} from "./util/i64";
|
||||
|
||||
/** Named token types. */
|
||||
export enum Token {
|
||||
|
||||
@ -993,21 +989,19 @@ export class Tokenizer extends DiagnosticEmitter {
|
||||
readHexInteger(): I64 {
|
||||
var text = this.source.text;
|
||||
var start = this.pos;
|
||||
var value = new I64(0, 0);
|
||||
var value = i64_new(0, 0);
|
||||
var i64_16 = i64_new(16, 0);
|
||||
while (this.pos < this.end) {
|
||||
var c = text.charCodeAt(this.pos);
|
||||
if (c >= CharCode._0 && c <= CharCode._9) {
|
||||
// value = value * 16 + c - CharCode._0;
|
||||
value.mul32(16);
|
||||
value.add32(c - CharCode._0);
|
||||
} else if (c >= CharCode.A && c <= CharCode.F) {
|
||||
value = i64_add(i64_mul(value, i64_16), i64_new(c - CharCode._0, 0));
|
||||
} else if (c >= CharCode.A && c <= CharCode.F) {
|
||||
// value = value * 16 + 10 + c - CharCode.A;
|
||||
value.mul32(16);
|
||||
value.add32(10 + c - CharCode.A);
|
||||
value = i64_add(i64_mul(value, i64_16), i64_new(10 + c - CharCode.A, 0));
|
||||
} else if (c >= CharCode.a && c <= CharCode.f) {
|
||||
// value = value * 16 + 10 + c - CharCode.a;
|
||||
value.mul32(16);
|
||||
value.add32(10 + c - CharCode.a);
|
||||
value = i64_add(i64_mul(value, i64_16), i64_new(10 + c - CharCode.a, 0));
|
||||
} else
|
||||
break;
|
||||
++this.pos;
|
||||
@ -1020,13 +1014,13 @@ export class Tokenizer extends DiagnosticEmitter {
|
||||
readDecimalInteger(): I64 {
|
||||
var text = this.source.text;
|
||||
var start = this.pos;
|
||||
var value = new I64(0, 0);
|
||||
var value = i64_new(0, 0);
|
||||
var i64_10 = i64_new(10, 0);
|
||||
while (this.pos < this.end) {
|
||||
var c = text.charCodeAt(this.pos);
|
||||
if (c >= CharCode._0 && c <= CharCode._9) {
|
||||
// value = value * 10 + c - CharCode._0;
|
||||
value.mul32(10);
|
||||
value.add32(c - CharCode._0);
|
||||
value = i64_add(i64_mul(value, i64_10), i64_new(c - CharCode._0, 0));
|
||||
} else
|
||||
break;
|
||||
++this.pos;
|
||||
@ -1039,13 +1033,13 @@ export class Tokenizer extends DiagnosticEmitter {
|
||||
readOctalInteger(): I64 {
|
||||
var text = this.source.text;
|
||||
var start = this.pos;
|
||||
var value = new I64(0, 0);
|
||||
var value = i64_new(0, 0);
|
||||
var i64_8 = i64_new(8, 0);
|
||||
while (this.pos < this.end) {
|
||||
var c = text.charCodeAt(this.pos);
|
||||
if (c >= CharCode._0 && c <= CharCode._7) {
|
||||
// value = value * 8 + c - CharCode._0;
|
||||
value.mul32(8);
|
||||
value.add32(c - CharCode._0);
|
||||
value = i64_add(i64_mul(value, i64_8), i64_new(c - CharCode._0, 0));
|
||||
} else
|
||||
break;
|
||||
++this.pos;
|
||||
@ -1058,18 +1052,18 @@ export class Tokenizer extends DiagnosticEmitter {
|
||||
readBinaryInteger(): I64 {
|
||||
var text = this.source.text;
|
||||
var start = this.pos;
|
||||
var value = new I64();
|
||||
var value = i64_new(0, 0);
|
||||
var i64_2 = i64_new(2, 0);
|
||||
var i64_1 = i64_new(1, 0);
|
||||
while (this.pos < this.end) {
|
||||
var c = text.charCodeAt(this.pos);
|
||||
if (c == CharCode._0) {
|
||||
// value = value * 2;
|
||||
value.mul32(2);
|
||||
value = i64_mul(value, i64_2);
|
||||
} else if (c == CharCode._1) {
|
||||
// value = value * 2 + 1;
|
||||
value.mul32(2);
|
||||
value.add32(1);
|
||||
}
|
||||
else
|
||||
value = i64_add(i64_mul(value, i64_2), i64_1);
|
||||
} else
|
||||
break;
|
||||
++this.pos;
|
||||
}
|
||||
@ -1129,14 +1123,15 @@ export class Tokenizer extends DiagnosticEmitter {
|
||||
private readExtendedUnicodeEscape(): string {
|
||||
var start = this.pos;
|
||||
var value = this.readHexInteger();
|
||||
var value32 = i64_low(value);
|
||||
var invalid = false;
|
||||
|
||||
if (value.gt32(0x10FFFF)) {
|
||||
assert(!i64_high(value));
|
||||
if (value32 > 0x10FFFF) {
|
||||
this.error(DiagnosticCode.An_extended_Unicode_escape_value_must_be_between_0x0_and_0x10FFFF_inclusive, this.range(start, this.pos));
|
||||
invalid = true;
|
||||
}
|
||||
|
||||
var value32 = value.toI32();
|
||||
var text = this.source.text;
|
||||
if (this.pos >= this.end) {
|
||||
this.error(DiagnosticCode.Unexpected_end_of_text, this.range(start, this.end));
|
||||
|
@ -8,6 +8,7 @@
|
||||
"./**/*.ts"
|
||||
],
|
||||
"exclude": [
|
||||
"./extra/**"
|
||||
"./extra/**",
|
||||
"./glue/wasm/**"
|
||||
]
|
||||
}
|
||||
|
537
src/util/i64.ts
537
src/util/i64.ts
@ -1,537 +0,0 @@
|
||||
/*
|
||||
|
||||
To remain compatible with TSC / compiling to JS, we have to emulate I64s in a
|
||||
portable way. The following is based on long.js with the main difference being
|
||||
that instances are mutable and operations affect 'this'. In our scenario,
|
||||
that's useful because it's mostly used for constant evaluation and we are
|
||||
exclusively interested in the result (saves a heap of allocations).
|
||||
|
||||
*/
|
||||
|
||||
// TODO: div/mod
|
||||
// another option is to use a wasm-based polyfill, see examples/i64-polyfill.
|
||||
|
||||
const I64_MIN_LO: i32 = 0;
|
||||
const I64_MIN_HI: i32 = 0x80000000 | 0;
|
||||
|
||||
export class I64 {
|
||||
|
||||
lo: i32;
|
||||
hi: i32;
|
||||
|
||||
static fromI32(n: i32): I64 {
|
||||
return new I64(n, n < 0 ? -1 : 0);
|
||||
}
|
||||
|
||||
constructor(lo: i32 = 0, hi: i32 = 0) {
|
||||
this.lo = lo;
|
||||
this.hi = hi;
|
||||
}
|
||||
|
||||
get isZero(): bool {
|
||||
return this.lo == 0 && this.hi == 0;
|
||||
}
|
||||
|
||||
get isOne(): bool {
|
||||
return this.lo == 1 && this.hi == 0;
|
||||
}
|
||||
|
||||
get isPositive(): bool {
|
||||
return this.hi >= 0;
|
||||
}
|
||||
|
||||
get isNegative(): bool {
|
||||
return this.hi < 0;
|
||||
}
|
||||
|
||||
get isOdd(): bool {
|
||||
return (this.lo & 1) == 1;
|
||||
}
|
||||
|
||||
get isEven(): bool {
|
||||
return (this.lo & 1) == 0;
|
||||
}
|
||||
|
||||
get fitsInI32(): bool {
|
||||
return this.hi == 0 || (this.hi == -1 && this.lo < 0);
|
||||
}
|
||||
|
||||
toI32(): i32 {
|
||||
return this.lo;
|
||||
}
|
||||
|
||||
toF64(): f64 {
|
||||
return <f64>this.hi * 0x100000000 + <f64>(this.lo >>> 0);
|
||||
}
|
||||
|
||||
eq(other: I64): bool {
|
||||
return this.eq32(other.lo, other.hi);
|
||||
}
|
||||
|
||||
eq32(lo: i32, hi: i32 = 0): bool {
|
||||
return this.lo == lo && this.hi == hi;
|
||||
}
|
||||
|
||||
ne(other: I64): bool {
|
||||
return this.ne32(other.lo, other.hi);
|
||||
}
|
||||
|
||||
ne32(lo: i32, hi: i32 = 0): bool {
|
||||
return this.lo != lo || this.hi != hi;
|
||||
}
|
||||
|
||||
neg(): void {
|
||||
this.lo = ~this.lo;
|
||||
this.hi = ~this.hi;
|
||||
this.add32(1, 0);
|
||||
}
|
||||
|
||||
add(other: I64): void {
|
||||
this.add32(other.lo, other.hi);
|
||||
}
|
||||
|
||||
add32(lo: i32, hi: i32 = 0): void {
|
||||
i64_add_internal(this.lo, this.hi, lo, hi);
|
||||
this.lo = i64_lo;
|
||||
this.hi = i64_hi;
|
||||
}
|
||||
|
||||
sub(other: I64): void {
|
||||
this.sub32(other.lo, other.hi);
|
||||
}
|
||||
|
||||
sub32(lo: i32, hi: i32 = 0): void {
|
||||
i64_add_internal(~lo, ~hi, 1, 0);
|
||||
this.add32(i64_lo, i64_hi);
|
||||
}
|
||||
|
||||
comp(other: I64): i32 {
|
||||
return this.comp32(other.lo, other.hi);
|
||||
}
|
||||
|
||||
comp32(lo: i32, hi: i32 = 0): i32 {
|
||||
if (this.lo == lo && this.hi == hi)
|
||||
return 0;
|
||||
if (this.hi < 0 && hi >= 0)
|
||||
return -1;
|
||||
if (this.hi >= 0 && hi < 0)
|
||||
return 1;
|
||||
i64_add_internal(~lo, ~hi, 1, 0);
|
||||
i64_add_internal(this.lo, this.hi, i64_lo, i64_hi);
|
||||
return i64_hi < 0 ? -1 : 1;
|
||||
}
|
||||
|
||||
lt(other: I64): bool {
|
||||
return this.lt32(other.lo, other.hi);
|
||||
}
|
||||
|
||||
lt32(lo: i32, hi: i32 = 0): bool {
|
||||
return this.comp32(lo, hi) < 0;
|
||||
}
|
||||
|
||||
lte(other: I64): bool {
|
||||
return this.lte32(other.lo, other.hi);
|
||||
}
|
||||
|
||||
lte32(lo: i32, hi: i32 = 0): bool {
|
||||
return this.comp32(lo, hi) <= 0;
|
||||
}
|
||||
|
||||
gt(other: I64): bool {
|
||||
return this.gt32(other.lo, other.hi);
|
||||
}
|
||||
|
||||
gt32(lo: i32, hi: i32 = 0): bool {
|
||||
return this.comp32(lo, hi) > 0;
|
||||
}
|
||||
|
||||
gte(other: I64): bool {
|
||||
return this.gte32(other.lo, other.hi);
|
||||
}
|
||||
|
||||
gte32(lo: i32, hi: i32 = 0): bool {
|
||||
return this.comp32(lo, hi) >= 0;
|
||||
}
|
||||
|
||||
mul(other: I64): void {
|
||||
this.mul32(other.lo, other.hi);
|
||||
}
|
||||
|
||||
mul32(lo: i32, hi: i32 = 0): void {
|
||||
if (this.lo == 0 && this.hi == 0)
|
||||
return;
|
||||
|
||||
if (lo == 0 && hi == 0) {
|
||||
this.lo = 0;
|
||||
this.hi = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
// this == MIN
|
||||
if (this.lo == I64_MIN_LO && this.hi == I64_MIN_HI) {
|
||||
this.lo = 0; // == MIN_LO
|
||||
this.hi = lo & 1 ? I64_MIN_HI : 0; // other.isOdd ? this = MIN : this = ZERO
|
||||
return;
|
||||
}
|
||||
|
||||
// other == MIN
|
||||
if (lo == I64_MIN_LO && hi == I64_MIN_HI) {
|
||||
this.hi = this.lo & 1 ? I64_MIN_HI : 0; // this.isOdd ? this = MIN : this = ZERO
|
||||
this.lo = 0; // == MIN_LO
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.hi < 0) {
|
||||
this.neg();
|
||||
|
||||
// both negative: negate both and multiply
|
||||
if (hi < 0) {
|
||||
i64_add_internal(~lo, ~hi, 1, 0);
|
||||
i64_mul_internal(this.lo, this.hi, i64_lo, i64_hi);
|
||||
this.lo = i64_lo;
|
||||
this.hi = i64_hi;
|
||||
|
||||
// this negative: negate this, multiply and negate result
|
||||
} else {
|
||||
i64_mul_internal(this.lo, this.hi, lo, hi);
|
||||
this.lo = i64_lo;
|
||||
this.hi = i64_hi;
|
||||
this.neg();
|
||||
}
|
||||
return;
|
||||
|
||||
// other negative: negate other, multiply and negate result
|
||||
} else if (hi < 0) {
|
||||
i64_add_internal(~lo, ~hi, 1, 0);
|
||||
i64_mul_internal(this.lo, this.hi, i64_lo, i64_hi);
|
||||
this.lo = i64_lo;
|
||||
this.hi = i64_hi;
|
||||
this.neg();
|
||||
return;
|
||||
}
|
||||
|
||||
// both positive
|
||||
i64_mul_internal(this.lo, this.hi, lo, hi);
|
||||
this.lo = i64_lo;
|
||||
this.hi = i64_hi;
|
||||
}
|
||||
|
||||
div(other: I64): void {
|
||||
this.div32(other.lo, other.hi);
|
||||
}
|
||||
|
||||
div32(lo: i32, hi: i32 = 0): void {
|
||||
// other == 0
|
||||
if (lo == 0 && hi == 0)
|
||||
throw new Error("division by zero");
|
||||
|
||||
// this == 0
|
||||
if (this.lo == 0 && this.hi == 0)
|
||||
return;
|
||||
|
||||
// this == MIN
|
||||
if (this.lo == I64_MIN_LO && this.hi == I64_MIN_HI) {
|
||||
|
||||
// other == 1 or -1
|
||||
if (lo == 1 && hi == 0 || lo == -1 && hi == -1) // -MIN == MIN
|
||||
return;
|
||||
|
||||
// both == MIN
|
||||
if (lo == I64_MIN_LO && hi == I64_MIN_HI) {
|
||||
this.lo = 1;
|
||||
this.hi = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
// |other| >= 2, so |this/other| < |MIN_VALUE|
|
||||
var tempLo = this.lo;
|
||||
var tempHi = this.hi;
|
||||
this.shr32(1, 0);
|
||||
this.div32(lo, hi);
|
||||
this.shl32(1, 0);
|
||||
if (this.lo == 0 && this.hi == 0) {
|
||||
if (hi < 0) {
|
||||
this.lo = 1;
|
||||
this.hi = 0;
|
||||
} else {
|
||||
this.lo = -1;
|
||||
this.hi = -1;
|
||||
}
|
||||
return;
|
||||
}
|
||||
i64_mul_internal(lo, hi, this.lo, this.hi);
|
||||
this.lo = tempLo;
|
||||
this.hi = tempHi;
|
||||
tempLo = i64_lo;
|
||||
tempHi = i64_hi;
|
||||
this.div32(lo, hi);
|
||||
this.sub32(i64_lo, i64_hi);
|
||||
i64_add_internal(tempLo, tempHi, this.lo, this.hi);
|
||||
this.lo = i64_lo;
|
||||
this.hi = i64_hi;
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.hi < 0) {
|
||||
this.neg();
|
||||
|
||||
// both negative: negate both and divide
|
||||
if (hi < 0) {
|
||||
i64_add_internal(~lo, ~hi, 1, 0);
|
||||
i64_div_internal(this.lo, this.hi, i64_lo, i64_hi);
|
||||
this.lo = i64_lo;
|
||||
this.hi = i64_hi;
|
||||
|
||||
// this negative: negate this, divide and negate result
|
||||
} else {
|
||||
i64_div_internal(this.lo, this.hi, lo, hi);
|
||||
this.lo = i64_lo;
|
||||
this.hi = i64_hi;
|
||||
this.neg();
|
||||
}
|
||||
return;
|
||||
|
||||
// other negative: negate other, divide and negate result
|
||||
} else if (hi < 0) {
|
||||
i64_add_internal(~lo, ~hi, 1, 0);
|
||||
i64_div_internal(this.lo, this.hi, i64_lo, i64_hi);
|
||||
this.lo = i64_lo;
|
||||
this.hi = i64_hi;
|
||||
this.neg();
|
||||
return;
|
||||
}
|
||||
|
||||
// both positive
|
||||
i64_div_internal(this.lo, this.hi, lo, hi);
|
||||
this.lo = i64_lo;
|
||||
this.hi = i64_hi;
|
||||
}
|
||||
|
||||
mod(other: I64): void {
|
||||
this.mod32(other.lo, other.hi);
|
||||
}
|
||||
|
||||
mod32(lo: i32, hi: i32 = 0): void {
|
||||
var thisLo = this.lo;
|
||||
var thisHi = this.hi;
|
||||
this.div32(lo, hi);
|
||||
this.mul32(lo, hi);
|
||||
var resLo = this.lo;
|
||||
var resHi = this.hi;
|
||||
this.lo = thisLo;
|
||||
this.hi = thisHi;
|
||||
this.sub32(resLo, resHi);
|
||||
}
|
||||
|
||||
not(): void {
|
||||
this.lo = ~this.lo;
|
||||
this.hi = ~this.hi;
|
||||
}
|
||||
|
||||
and(other: I64): void {
|
||||
this.and32(other.lo, other.hi);
|
||||
}
|
||||
|
||||
and32(lo: i32, hi: i32 = 0): void {
|
||||
this.lo &= lo;
|
||||
this.hi &= hi;
|
||||
}
|
||||
|
||||
or(other: I64): void {
|
||||
this.or32(other.lo, other.hi);
|
||||
}
|
||||
|
||||
or32(lo: i32, hi: i32 = 0): void {
|
||||
this.lo |= lo;
|
||||
this.hi |= hi;
|
||||
}
|
||||
|
||||
xor(other: I64): void {
|
||||
this.xor32(other.lo, other.hi);
|
||||
}
|
||||
|
||||
xor32(lo: i32, hi: i32 = 0): void {
|
||||
this.lo ^= lo;
|
||||
this.hi ^= hi;
|
||||
}
|
||||
|
||||
shl(other: I64): void {
|
||||
this.shl32(other.lo, other.hi);
|
||||
}
|
||||
|
||||
shl32(lo: i32, hi: i32 = 0): void {
|
||||
if ((lo &= 63) == 0)
|
||||
return;
|
||||
if (lo < 32) {
|
||||
this.hi = (this.hi << lo) | (this.lo >>> (32 - lo));
|
||||
this.lo = this.lo << lo;
|
||||
} else {
|
||||
this.hi = this.lo << (lo - 32);
|
||||
this.lo = 0;
|
||||
}
|
||||
}
|
||||
|
||||
shr(other: I64): void {
|
||||
this.shr32(other.lo, other.hi);
|
||||
}
|
||||
|
||||
shr32(lo: i32, hi: i32 = 0): void {
|
||||
if ((lo &= 63) == 0)
|
||||
return;
|
||||
if (lo < 32) {
|
||||
this.lo = (this.lo >>> lo) | (this.hi << (32 - lo));
|
||||
this.hi = this.hi >> lo;
|
||||
} else {
|
||||
this.lo = this.hi >> (lo - 32);
|
||||
this.hi = this.hi >= 0 ? 0 : -1;
|
||||
}
|
||||
}
|
||||
|
||||
shru(other: I64): void {
|
||||
this.shru32(other.lo, other.hi);
|
||||
}
|
||||
|
||||
shru32(lo: i32, hi: i32 = 0): void {
|
||||
if ((lo &= 63) == 0)
|
||||
return;
|
||||
if (lo < 32) {
|
||||
this.lo = (this.lo >>> lo) | (this.hi << (32 - lo));
|
||||
this.hi = (this.hi >>> lo) | 0;
|
||||
} else if (lo == 32) {
|
||||
this.lo = this.hi;
|
||||
this.hi = 0;
|
||||
} else {
|
||||
this.lo = (this.hi >>> (lo - 32)) | 0;
|
||||
this.hi = 0;
|
||||
}
|
||||
}
|
||||
|
||||
clone(): I64 {
|
||||
return new I64(this.lo, this.hi);
|
||||
}
|
||||
|
||||
toString(): string {
|
||||
var negative = false;
|
||||
if (this.hi < 0) {
|
||||
i64_add_internal(~this.lo, ~this.hi, 1, 0);
|
||||
negative = true;
|
||||
} else {
|
||||
i64_lo = this.lo;
|
||||
i64_hi = this.hi;
|
||||
}
|
||||
|
||||
if (i64_hi) {
|
||||
var lo = (i64_lo as u32 >>> 0).toString(16);
|
||||
while (lo.length < 8)
|
||||
lo = "0" + lo;
|
||||
return (negative ? "-0x" : "0x") + (i64_hi as u32 >>> 0).toString(16) + lo;
|
||||
}
|
||||
return negative ? "-" + i64_lo.toString(10) : i64_lo.toString(10);
|
||||
}
|
||||
}
|
||||
|
||||
var i64_lo = 0;
|
||||
var i64_hi = 0;
|
||||
|
||||
function i64_add_internal(lo: i32, hi: i32, otherLo: i32, otherHi: i32): void {
|
||||
var a48 = hi >>> 16;
|
||||
var a32 = hi & 0xFFFF;
|
||||
var a16 = lo >>> 16;
|
||||
var a00 = lo & 0xFFFF;
|
||||
|
||||
var b48 = otherHi >>> 16;
|
||||
var b32 = otherHi & 0xFFFF;
|
||||
var b16 = otherLo >>> 16;
|
||||
var b00 = otherLo & 0xFFFF;
|
||||
|
||||
var c48 = 0, c32 = 0, c16 = 0, c00 = 0;
|
||||
c00 += a00 + b00;
|
||||
c16 += c00 >>> 16;
|
||||
c00 &= 0xFFFF;
|
||||
c16 += a16 + b16;
|
||||
c32 += c16 >>> 16;
|
||||
c16 &= 0xFFFF;
|
||||
c32 += a32 + b32;
|
||||
c48 += c32 >>> 16;
|
||||
c32 &= 0xFFFF;
|
||||
c48 += a48 + b48;
|
||||
c48 &= 0xFFFF;
|
||||
|
||||
i64_lo = (c16 << 16) | c00;
|
||||
i64_hi = (c48 << 16) | c32;
|
||||
}
|
||||
|
||||
function i64_mul_internal(lo: i32, hi: i32, otherLo: i32, otherHi: i32): void {
|
||||
var a48 = hi >>> 16;
|
||||
var a32 = hi & 0xFFFF;
|
||||
var a16 = lo >>> 16;
|
||||
var a00 = lo & 0xFFFF;
|
||||
|
||||
var b48 = otherHi >>> 16;
|
||||
var b32 = otherHi & 0xFFFF;
|
||||
var b16 = otherLo >>> 16;
|
||||
var b00 = otherLo & 0xFFFF;
|
||||
|
||||
var c48 = 0, c32 = 0, c16 = 0, c00 = 0;
|
||||
c00 += a00 * b00;
|
||||
c16 += c00 >>> 16;
|
||||
c00 &= 0xFFFF;
|
||||
c16 += a16 * b00;
|
||||
c32 += c16 >>> 16;
|
||||
c16 &= 0xFFFF;
|
||||
c16 += a00 * b16;
|
||||
c32 += c16 >>> 16;
|
||||
c16 &= 0xFFFF;
|
||||
c32 += a32 * b00;
|
||||
c48 += c32 >>> 16;
|
||||
c32 &= 0xFFFF;
|
||||
c32 += a16 * b16;
|
||||
c48 += c32 >>> 16;
|
||||
c32 &= 0xFFFF;
|
||||
c32 += a00 * b32;
|
||||
c48 += c32 >>> 16;
|
||||
c32 &= 0xFFFF;
|
||||
c48 += a48 * b00 + a32 * b16 + a16 * b32 + a00 * b48;
|
||||
c48 &= 0xFFFF;
|
||||
|
||||
i64_lo = (c16 << 16) | c00;
|
||||
i64_hi = (c48 << 16) | c32;
|
||||
}
|
||||
|
||||
function i64_div_internal(lo: i32, hi: i32, otherLo: i32, otherHi: i32): void {
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
|
||||
export class U64 extends I64 {
|
||||
|
||||
static fromI32(n: i32): U64 {
|
||||
return new U64(n, 0);
|
||||
}
|
||||
|
||||
get isPositive(): bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
get isNegative(): bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
get fitsInU32(): bool {
|
||||
return this.hi == 0;
|
||||
}
|
||||
|
||||
comp32(lo: i32, hi: i32): i32 {
|
||||
// uses both a cast and a js-like shift for portability
|
||||
return ((hi as u32 >>> 0) > (this.hi as u32 >>> 0)) || (hi == this.hi && (lo as u32 >>> 0) > (this.lo as u32 >>> 0)) ? -1 : 1;
|
||||
}
|
||||
|
||||
neg(): void {
|
||||
this.lo = ~this.lo;
|
||||
this.hi = ~this.hi;
|
||||
this.add32(1, 0);
|
||||
}
|
||||
|
||||
clone(): U64 {
|
||||
return new U64(this.lo, this.hi);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user