mirror of
https://github.com/fluencelabs/assemblyscript
synced 2025-04-25 15:12:12 +00:00
Tackle AS206
This commit is contained in:
parent
c06d5d9f9a
commit
bc0dd3a6fb
@ -779,7 +779,7 @@ export class Compiler extends DiagnosticEmitter {
|
||||
|
||||
if (!global.is(CommonFlags.RESOLVED)) {
|
||||
|
||||
// resolve now if annotated
|
||||
// Resolve type if annotated
|
||||
if (typeNode) {
|
||||
let resolvedType = this.resolver.resolveType(typeNode, global.parent); // reports
|
||||
if (!resolvedType) return false;
|
||||
@ -792,8 +792,8 @@ export class Compiler extends DiagnosticEmitter {
|
||||
}
|
||||
global.setType(resolvedType);
|
||||
|
||||
// infer from initializer if not annotated
|
||||
} else if (initializerNode) { // infer type using void/NONE for literal inference
|
||||
// Otherwise infer type from initializer
|
||||
} else if (initializerNode) {
|
||||
let previousFlow = this.currentFlow;
|
||||
if (global.hasDecorator(DecoratorFlags.LAZY)) {
|
||||
this.currentFlow = global.file.startFunction.flow;
|
||||
@ -812,7 +812,7 @@ export class Compiler extends DiagnosticEmitter {
|
||||
}
|
||||
global.setType(this.currentType);
|
||||
|
||||
// must either be annotated or have an initializer
|
||||
// Error if there's neither a type nor an initializer
|
||||
} else {
|
||||
this.error(
|
||||
DiagnosticCode.Type_expected,
|
||||
@ -822,7 +822,7 @@ export class Compiler extends DiagnosticEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
// ambient builtins like 'HEAP_BASE' need to be resolved but are added explicitly
|
||||
// Handle ambient builtins like 'HEAP_BASE' that need to be resolved but are added explicitly
|
||||
if (global.is(CommonFlags.AMBIENT) && global.hasDecorator(DecoratorFlags.BUILTIN)) {
|
||||
if (global.internalName == BuiltinSymbols.HEAP_BASE) this.runtimeFeatures |= RuntimeFeatures.HEAP;
|
||||
else if (global.internalName == BuiltinSymbols.RTTI_BASE) this.runtimeFeatures |= RuntimeFeatures.RTTI;
|
||||
@ -832,11 +832,12 @@ export class Compiler extends DiagnosticEmitter {
|
||||
var type = global.type;
|
||||
var nativeType = type.toNativeType();
|
||||
var isDeclaredConstant = global.is(CommonFlags.CONST) || global.is(CommonFlags.STATIC | CommonFlags.READONLY);
|
||||
var isDeclaredInline = global.hasDecorator(DecoratorFlags.INLINE);
|
||||
|
||||
// handle imports
|
||||
// Handle imports
|
||||
if (global.is(CommonFlags.AMBIENT)) {
|
||||
|
||||
// constant global
|
||||
// Constant global or mutable globals enabled
|
||||
if (isDeclaredConstant || this.options.hasFeature(Feature.MUTABLE_GLOBAL)) {
|
||||
global.set(CommonFlags.MODULE_IMPORT);
|
||||
mangleImportName(global, global.declaration);
|
||||
@ -849,7 +850,7 @@ export class Compiler extends DiagnosticEmitter {
|
||||
global.set(CommonFlags.COMPILED);
|
||||
return true;
|
||||
|
||||
// importing mutable globals is not supported in the MVP
|
||||
// Importing mutable globals is not supported in the MVP
|
||||
} else {
|
||||
this.error(
|
||||
DiagnosticCode.Operation_not_supported,
|
||||
@ -859,11 +860,11 @@ export class Compiler extends DiagnosticEmitter {
|
||||
return false;
|
||||
}
|
||||
|
||||
// the MVP does not yet support initializer expressions other than constant values (and constant
|
||||
// The MVP does not yet support initializer expressions other than constant values (and constant
|
||||
// get_globals), hence such initializations must be performed in the start function for now.
|
||||
var initializeInStart = false;
|
||||
|
||||
// evaluate initializer if present
|
||||
// Evaluate initializer if present
|
||||
if (initializerNode) {
|
||||
if (!initExpr) {
|
||||
let previousFlow = this.currentFlow;
|
||||
@ -880,21 +881,20 @@ export class Compiler extends DiagnosticEmitter {
|
||||
if (getExpressionId(initExpr) != ExpressionId.Const) {
|
||||
if (isDeclaredConstant) {
|
||||
initExpr = module.precomputeExpression(initExpr);
|
||||
if (getExpressionId(initExpr) != ExpressionId.Const) {
|
||||
this.warning(
|
||||
DiagnosticCode.Compiling_constant_with_non_constant_initializer_as_mutable,
|
||||
initializerNode.range
|
||||
);
|
||||
initializeInStart = true;
|
||||
}
|
||||
if (getExpressionId(initExpr) != ExpressionId.Const) initializeInStart = true;
|
||||
} else {
|
||||
initializeInStart = true;
|
||||
}
|
||||
}
|
||||
|
||||
// explicitly inline if annotated
|
||||
if (global.hasDecorator(DecoratorFlags.INLINE)) {
|
||||
if (!initializeInStart) { // reported above
|
||||
// Explicitly inline if annotated
|
||||
if (isDeclaredInline) {
|
||||
if (initializeInStart) {
|
||||
this.warning(
|
||||
DiagnosticCode.Mutable_value_cannot_be_inlined,
|
||||
initializerNode.range
|
||||
);
|
||||
} else {
|
||||
assert(getExpressionId(initExpr) == ExpressionId.Const);
|
||||
let exprType = getExpressionType(initExpr);
|
||||
switch (exprType) {
|
||||
@ -930,7 +930,7 @@ export class Compiler extends DiagnosticEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
// initialize to zero if there's no initializer
|
||||
// Initialize to zero if there's no initializer
|
||||
} else {
|
||||
initExpr = type.toNativeZero(module);
|
||||
}
|
||||
@ -938,7 +938,7 @@ export class Compiler extends DiagnosticEmitter {
|
||||
var internalName = global.internalName;
|
||||
|
||||
if (initializeInStart) { // initialize to mutable zero and set the actual value in start
|
||||
if (global.hasDecorator(DecoratorFlags.INLINE)) {
|
||||
if (isDeclaredInline) {
|
||||
this.error(
|
||||
DiagnosticCode.Decorator_0_is_not_valid_here,
|
||||
assert(findDecorator(DecoratorKind.INLINE, global.decoratorNodes)).range, "inline"
|
||||
@ -949,7 +949,7 @@ export class Compiler extends DiagnosticEmitter {
|
||||
this.currentBody.push(
|
||||
module.global_set(internalName, initExpr)
|
||||
);
|
||||
} else if (!global.hasDecorator(DecoratorFlags.INLINE)) { // compile normally
|
||||
} else if (!isDeclaredInline) { // compile normally
|
||||
module.addGlobal(internalName, nativeType, !isDeclaredConstant, initExpr);
|
||||
}
|
||||
return true;
|
||||
@ -2406,12 +2406,15 @@ export class Compiler extends DiagnosticEmitter {
|
||||
var flow = this.currentFlow;
|
||||
var initializers = new Array<ExpressionRef>();
|
||||
var resolver = this.resolver;
|
||||
|
||||
for (let i = 0; i < numDeclarations; ++i) {
|
||||
let declaration = declarations[i];
|
||||
let name = declaration.name.text;
|
||||
let type: Type | null = null;
|
||||
let initExpr: ExpressionRef = 0;
|
||||
let initAutoreleaseSkipped = false;
|
||||
|
||||
// Resolve type if annotated
|
||||
if (declaration.type) {
|
||||
type = resolver.resolveType( // reports
|
||||
declaration.type,
|
||||
@ -2425,7 +2428,9 @@ export class Compiler extends DiagnosticEmitter {
|
||||
);
|
||||
initAutoreleaseSkipped = this.skippedAutoreleases.has(initExpr);
|
||||
}
|
||||
} else if (declaration.initializer) { // infer type using void/NONE for proper literal inference
|
||||
|
||||
// Otherwise infer type from initializer
|
||||
} else if (declaration.initializer) {
|
||||
initExpr = this.compileExpressionRetainType(declaration.initializer, Type.void,
|
||||
ContextualFlags.SKIP_AUTORELEASE
|
||||
); // reports
|
||||
@ -2438,6 +2443,8 @@ export class Compiler extends DiagnosticEmitter {
|
||||
continue;
|
||||
}
|
||||
type = this.currentType;
|
||||
|
||||
// Error if there's neither a type nor an initializer
|
||||
} else {
|
||||
this.error(
|
||||
DiagnosticCode.Type_expected,
|
||||
@ -2445,8 +2452,11 @@ export class Compiler extends DiagnosticEmitter {
|
||||
);
|
||||
continue;
|
||||
}
|
||||
let isInlined = false;
|
||||
if (declaration.is(CommonFlags.CONST)) {
|
||||
|
||||
// Handle constants, and try to inline if value is static
|
||||
let isConst = declaration.is(CommonFlags.CONST);
|
||||
let isStatic = false;
|
||||
if (isConst) {
|
||||
if (initExpr) {
|
||||
initExpr = module.precomputeExpression(initExpr);
|
||||
if (getExpressionId(initExpr) == ExpressionId.Const) {
|
||||
@ -2496,12 +2506,7 @@ export class Compiler extends DiagnosticEmitter {
|
||||
return this.module.unreachable();
|
||||
}
|
||||
scopedLocals.set(name, local);
|
||||
isInlined = true;
|
||||
} else {
|
||||
this.warning(
|
||||
DiagnosticCode.Compiling_constant_with_non_constant_initializer_as_mutable,
|
||||
declaration.range
|
||||
);
|
||||
isStatic = true;
|
||||
}
|
||||
} else {
|
||||
this.error(
|
||||
@ -2510,13 +2515,16 @@ export class Compiler extends DiagnosticEmitter {
|
||||
);
|
||||
}
|
||||
}
|
||||
if (!isInlined) {
|
||||
|
||||
// Otherwise compile as mutable
|
||||
if (!isStatic) {
|
||||
let local: Local;
|
||||
if (
|
||||
declaration.isAny(CommonFlags.LET | CommonFlags.CONST) ||
|
||||
flow.is(FlowFlags.INLINE_CONTEXT)
|
||||
) { // here: not top-level
|
||||
local = flow.addScopedLocal(name, type, declaration.name); // reports if duplicate
|
||||
if (isConst) flow.setLocalFlag(local.index, LocalFlags.CONSTANT);
|
||||
} else {
|
||||
if (flow.lookupLocal(name)) {
|
||||
this.error(
|
||||
@ -2526,6 +2534,7 @@ export class Compiler extends DiagnosticEmitter {
|
||||
continue;
|
||||
}
|
||||
local = flow.parentFunction.addLocal(type, name, declaration);
|
||||
if (isConst) flow.setLocalFlag(local.index, LocalFlags.CONSTANT);
|
||||
}
|
||||
let isManaged = type.isManaged;
|
||||
if (initExpr) {
|
||||
@ -5234,7 +5243,7 @@ export class Compiler extends DiagnosticEmitter {
|
||||
|
||||
switch (target.kind) {
|
||||
case ElementKind.LOCAL: {
|
||||
if (target.is(CommonFlags.CONST)) {
|
||||
if (flow.isLocalFlag((<Local>target).index, LocalFlags.CONSTANT, true)) {
|
||||
this.error(
|
||||
DiagnosticCode.Cannot_assign_to_0_because_it_is_a_constant_or_a_read_only_property,
|
||||
valueExpression.range, target.internalName
|
||||
@ -5246,7 +5255,7 @@ export class Compiler extends DiagnosticEmitter {
|
||||
}
|
||||
case ElementKind.GLOBAL: {
|
||||
if (!this.compileGlobal(<Global>target)) return module.unreachable();
|
||||
if (target.is(CommonFlags.CONST)) {
|
||||
if (target.isAny(CommonFlags.CONST | CommonFlags.READONLY)) {
|
||||
this.error(
|
||||
DiagnosticCode.Cannot_assign_to_0_because_it_is_a_constant_or_a_read_only_property,
|
||||
valueExpression.range,
|
||||
@ -7534,7 +7543,7 @@ export class Compiler extends DiagnosticEmitter {
|
||||
// compile value expressions and find out whether all are constant
|
||||
var length = expressions.length;
|
||||
var values = new Array<ExpressionRef>(length);
|
||||
var allValuesAreConstant = true;
|
||||
var isStatic = true;
|
||||
var nativeElementType = elementType.toNativeType();
|
||||
for (let i = 0; i < length; ++i) {
|
||||
let expression = expressions[i];
|
||||
@ -7548,19 +7557,13 @@ export class Compiler extends DiagnosticEmitter {
|
||||
if (getExpressionId(expr) == ExpressionId.Const) {
|
||||
assert(getExpressionType(expr) == nativeElementType);
|
||||
} else {
|
||||
if (isConst) {
|
||||
this.warning(
|
||||
DiagnosticCode.Compiling_constant_with_non_constant_initializer_as_mutable,
|
||||
reportNode.range
|
||||
);
|
||||
}
|
||||
allValuesAreConstant = false;
|
||||
isStatic = false;
|
||||
}
|
||||
values[i] = expr;
|
||||
}
|
||||
|
||||
// if the array is static, make a static arraybuffer segment
|
||||
if (allValuesAreConstant) {
|
||||
if (isStatic) {
|
||||
flow.freeTempLocal(tempThis);
|
||||
flow.freeTempLocal(tempDataStart);
|
||||
|
||||
|
@ -16,7 +16,7 @@ export enum DiagnosticCode {
|
||||
Type_0_cannot_be_reinterpreted_as_type_1 = 203,
|
||||
Basic_type_0_cannot_be_nullable = 204,
|
||||
Cannot_export_a_mutable_global = 205,
|
||||
Compiling_constant_with_non_constant_initializer_as_mutable = 206,
|
||||
Mutable_value_cannot_be_inlined = 206,
|
||||
Unmanaged_classes_cannot_extend_managed_classes_and_vice_versa = 207,
|
||||
Unmanaged_classes_cannot_implement_interfaces = 208,
|
||||
Invalid_regular_expression_flags = 209,
|
||||
@ -154,7 +154,7 @@ export function diagnosticCodeToString(code: DiagnosticCode): string {
|
||||
case 203: return "Type '{0}' cannot be reinterpreted as type '{1}'.";
|
||||
case 204: return "Basic type '{0}' cannot be nullable.";
|
||||
case 205: return "Cannot export a mutable global.";
|
||||
case 206: return "Compiling constant with non-constant initializer as mutable.";
|
||||
case 206: return "Mutable value cannot be inlined.";
|
||||
case 207: return "Unmanaged classes cannot extend managed classes and vice-versa.";
|
||||
case 208: return "Unmanaged classes cannot implement interfaces.";
|
||||
case 209: return "Invalid regular expression flags.";
|
||||
|
@ -8,7 +8,7 @@
|
||||
"Type '{0}' cannot be reinterpreted as type '{1}'.": 203,
|
||||
"Basic type '{0}' cannot be nullable.": 204,
|
||||
"Cannot export a mutable global.": 205,
|
||||
"Compiling constant with non-constant initializer as mutable.": 206,
|
||||
"Mutable value cannot be inlined.": 206,
|
||||
"Unmanaged classes cannot extend managed classes and vice-versa.": 207,
|
||||
"Unmanaged classes cannot implement interfaces.": 208,
|
||||
"Invalid regular expression flags.": 209,
|
||||
|
21
src/flow.ts
21
src/flow.ts
@ -143,26 +143,29 @@ export enum LocalFlags {
|
||||
/** No specific conditions. */
|
||||
NONE = 0,
|
||||
|
||||
/** Local is constant. */
|
||||
CONSTANT = 1 << 0,
|
||||
/** Local is properly wrapped. Relevant for small integers. */
|
||||
WRAPPED = 1 << 0,
|
||||
WRAPPED = 1 << 1,
|
||||
/** Local is non-null. */
|
||||
NONNULL = 1 << 1,
|
||||
NONNULL = 1 << 2,
|
||||
/** Local is read from. */
|
||||
READFROM = 1 << 2,
|
||||
READFROM = 1 << 3,
|
||||
/** Local is written to. */
|
||||
WRITTENTO = 1 << 3,
|
||||
WRITTENTO = 1 << 4,
|
||||
/** Local is retained. */
|
||||
RETAINED = 1 << 4,
|
||||
RETAINED = 1 << 5,
|
||||
|
||||
/** Local is conditionally read from. */
|
||||
CONDITIONALLY_READFROM = 1 << 5,
|
||||
CONDITIONALLY_READFROM = 1 << 6,
|
||||
/** Local is conditionally written to. */
|
||||
CONDITIONALLY_WRITTENTO = 1 << 6,
|
||||
CONDITIONALLY_WRITTENTO = 1 << 7,
|
||||
/** Local must be conditionally retained. */
|
||||
CONDITIONALLY_RETAINED = 1 << 7,
|
||||
CONDITIONALLY_RETAINED = 1 << 8,
|
||||
|
||||
/** Any categorical flag. */
|
||||
ANY_CATEGORICAL = WRAPPED
|
||||
ANY_CATEGORICAL = CONSTANT
|
||||
| WRAPPED
|
||||
| NONNULL
|
||||
| READFROM
|
||||
| WRITTENTO
|
||||
|
5
tests/compiler/constant-assign.json
Normal file
5
tests/compiler/constant-assign.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"asc_flags": [
|
||||
"--runtime none"
|
||||
]
|
||||
}
|
319
tests/compiler/constant-assign.optimized.wat
Normal file
319
tests/compiler/constant-assign.optimized.wat
Normal file
@ -0,0 +1,319 @@
|
||||
(module
|
||||
(type $FUNCSIG$iii (func (param i32 i32) (result i32)))
|
||||
(type $FUNCSIG$v (func))
|
||||
(type $FUNCSIG$vii (func (param i32 i32)))
|
||||
(type $FUNCSIG$ii (func (param i32) (result i32)))
|
||||
(memory $0 1)
|
||||
(data (i32.const 8) "\04\00\00\00\01\00\00\00\00\00\00\00\04\00\00\00\02")
|
||||
(data (i32.const 32) "\04\00\00\00\01\00\00\00\00\00\00\00\04\00\00\00\01")
|
||||
(data (i32.const 56) "\04\00\00\00\01\00\00\00\00\00\00\00\04\00\00\00\02")
|
||||
(global $~lib/rt/stub/startOffset (mut i32) (i32.const 0))
|
||||
(global $~lib/rt/stub/offset (mut i32) (i32.const 0))
|
||||
(export "memory" (memory $0))
|
||||
(start $start)
|
||||
(func $~lib/rt/stub/__alloc (; 0 ;) (type $FUNCSIG$iii) (param $0 i32) (param $1 i32) (result i32)
|
||||
(local $2 i32)
|
||||
(local $3 i32)
|
||||
(local $4 i32)
|
||||
(local $5 i32)
|
||||
local.get $0
|
||||
i32.const 1073741808
|
||||
i32.gt_u
|
||||
if
|
||||
unreachable
|
||||
end
|
||||
global.get $~lib/rt/stub/offset
|
||||
i32.const 16
|
||||
i32.add
|
||||
local.tee $3
|
||||
local.get $0
|
||||
i32.const 1
|
||||
local.get $0
|
||||
i32.const 1
|
||||
i32.gt_u
|
||||
select
|
||||
i32.add
|
||||
i32.const 15
|
||||
i32.add
|
||||
i32.const -16
|
||||
i32.and
|
||||
local.tee $2
|
||||
memory.size
|
||||
local.tee $4
|
||||
i32.const 16
|
||||
i32.shl
|
||||
i32.gt_u
|
||||
if
|
||||
local.get $4
|
||||
local.get $2
|
||||
local.get $3
|
||||
i32.sub
|
||||
i32.const 65535
|
||||
i32.add
|
||||
i32.const -65536
|
||||
i32.and
|
||||
i32.const 16
|
||||
i32.shr_u
|
||||
local.tee $5
|
||||
local.get $4
|
||||
local.get $5
|
||||
i32.gt_s
|
||||
select
|
||||
memory.grow
|
||||
i32.const 0
|
||||
i32.lt_s
|
||||
if
|
||||
local.get $5
|
||||
memory.grow
|
||||
i32.const 0
|
||||
i32.lt_s
|
||||
if
|
||||
unreachable
|
||||
end
|
||||
end
|
||||
end
|
||||
local.get $2
|
||||
global.set $~lib/rt/stub/offset
|
||||
local.get $3
|
||||
i32.const 16
|
||||
i32.sub
|
||||
local.tee $2
|
||||
local.get $1
|
||||
i32.store offset=8
|
||||
local.get $2
|
||||
local.get $0
|
||||
i32.store offset=12
|
||||
local.get $3
|
||||
)
|
||||
(func $~lib/memory/memory.copy (; 1 ;) (type $FUNCSIG$vii) (param $0 i32) (param $1 i32)
|
||||
(local $2 i32)
|
||||
(local $3 i32)
|
||||
(local $4 i32)
|
||||
block $~lib/util/memory/memmove|inlined.0
|
||||
i32.const 4
|
||||
local.set $2
|
||||
local.get $0
|
||||
local.get $1
|
||||
i32.eq
|
||||
br_if $~lib/util/memory/memmove|inlined.0
|
||||
local.get $0
|
||||
local.get $1
|
||||
i32.lt_u
|
||||
if
|
||||
local.get $1
|
||||
i32.const 7
|
||||
i32.and
|
||||
local.get $0
|
||||
i32.const 7
|
||||
i32.and
|
||||
i32.eq
|
||||
if
|
||||
loop $continue|0
|
||||
local.get $0
|
||||
i32.const 7
|
||||
i32.and
|
||||
if
|
||||
local.get $2
|
||||
i32.eqz
|
||||
br_if $~lib/util/memory/memmove|inlined.0
|
||||
local.get $2
|
||||
i32.const 1
|
||||
i32.sub
|
||||
local.set $2
|
||||
local.get $0
|
||||
local.tee $3
|
||||
i32.const 1
|
||||
i32.add
|
||||
local.set $0
|
||||
local.get $1
|
||||
local.tee $4
|
||||
i32.const 1
|
||||
i32.add
|
||||
local.set $1
|
||||
local.get $3
|
||||
local.get $4
|
||||
i32.load8_u
|
||||
i32.store8
|
||||
br $continue|0
|
||||
end
|
||||
end
|
||||
loop $continue|1
|
||||
local.get $2
|
||||
i32.const 8
|
||||
i32.ge_u
|
||||
if
|
||||
local.get $0
|
||||
local.get $1
|
||||
i64.load
|
||||
i64.store
|
||||
local.get $2
|
||||
i32.const 8
|
||||
i32.sub
|
||||
local.set $2
|
||||
local.get $0
|
||||
i32.const 8
|
||||
i32.add
|
||||
local.set $0
|
||||
local.get $1
|
||||
i32.const 8
|
||||
i32.add
|
||||
local.set $1
|
||||
br $continue|1
|
||||
end
|
||||
end
|
||||
end
|
||||
loop $continue|2
|
||||
local.get $2
|
||||
if
|
||||
local.get $0
|
||||
local.tee $3
|
||||
i32.const 1
|
||||
i32.add
|
||||
local.set $0
|
||||
local.get $1
|
||||
local.tee $4
|
||||
i32.const 1
|
||||
i32.add
|
||||
local.set $1
|
||||
local.get $3
|
||||
local.get $4
|
||||
i32.load8_u
|
||||
i32.store8
|
||||
local.get $2
|
||||
i32.const 1
|
||||
i32.sub
|
||||
local.set $2
|
||||
br $continue|2
|
||||
end
|
||||
end
|
||||
else
|
||||
local.get $1
|
||||
i32.const 7
|
||||
i32.and
|
||||
local.get $0
|
||||
i32.const 7
|
||||
i32.and
|
||||
i32.eq
|
||||
if
|
||||
loop $continue|3
|
||||
local.get $0
|
||||
local.get $2
|
||||
i32.add
|
||||
i32.const 7
|
||||
i32.and
|
||||
if
|
||||
local.get $2
|
||||
i32.eqz
|
||||
br_if $~lib/util/memory/memmove|inlined.0
|
||||
local.get $2
|
||||
i32.const 1
|
||||
i32.sub
|
||||
local.tee $2
|
||||
local.get $0
|
||||
i32.add
|
||||
local.get $1
|
||||
local.get $2
|
||||
i32.add
|
||||
i32.load8_u
|
||||
i32.store8
|
||||
br $continue|3
|
||||
end
|
||||
end
|
||||
loop $continue|4
|
||||
local.get $2
|
||||
i32.const 8
|
||||
i32.ge_u
|
||||
if
|
||||
local.get $2
|
||||
i32.const 8
|
||||
i32.sub
|
||||
local.tee $2
|
||||
local.get $0
|
||||
i32.add
|
||||
local.get $1
|
||||
local.get $2
|
||||
i32.add
|
||||
i64.load
|
||||
i64.store
|
||||
br $continue|4
|
||||
end
|
||||
end
|
||||
end
|
||||
loop $continue|5
|
||||
local.get $2
|
||||
if
|
||||
local.get $2
|
||||
i32.const 1
|
||||
i32.sub
|
||||
local.tee $2
|
||||
local.get $0
|
||||
i32.add
|
||||
local.get $1
|
||||
local.get $2
|
||||
i32.add
|
||||
i32.load8_u
|
||||
i32.store8
|
||||
br $continue|5
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
)
|
||||
(func $~lib/rt/__allocArray (; 2 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32)
|
||||
(local $1 i32)
|
||||
(local $2 i32)
|
||||
i32.const 16
|
||||
i32.const 3
|
||||
call $~lib/rt/stub/__alloc
|
||||
local.tee $1
|
||||
i32.const 4
|
||||
i32.const 0
|
||||
call $~lib/rt/stub/__alloc
|
||||
local.tee $2
|
||||
i32.store
|
||||
local.get $1
|
||||
local.get $2
|
||||
i32.store offset=4
|
||||
local.get $1
|
||||
i32.const 4
|
||||
i32.store offset=8
|
||||
local.get $1
|
||||
i32.const 1
|
||||
i32.store offset=12
|
||||
local.get $0
|
||||
if
|
||||
local.get $2
|
||||
local.get $0
|
||||
call $~lib/memory/memory.copy
|
||||
end
|
||||
local.get $1
|
||||
)
|
||||
(func $start:constant-assign (; 3 ;) (type $FUNCSIG$v)
|
||||
i32.const 80
|
||||
global.set $~lib/rt/stub/startOffset
|
||||
global.get $~lib/rt/stub/startOffset
|
||||
global.set $~lib/rt/stub/offset
|
||||
i32.const 0
|
||||
call $~lib/rt/__allocArray
|
||||
i32.load offset=4
|
||||
i32.const 1
|
||||
i32.store
|
||||
i32.const 24
|
||||
call $~lib/rt/__allocArray
|
||||
drop
|
||||
i32.const 48
|
||||
call $~lib/rt/__allocArray
|
||||
drop
|
||||
i32.const 72
|
||||
call $~lib/rt/__allocArray
|
||||
drop
|
||||
)
|
||||
(func $start (; 4 ;) (type $FUNCSIG$v)
|
||||
block
|
||||
call $start:constant-assign
|
||||
end
|
||||
)
|
||||
(func $null (; 5 ;) (type $FUNCSIG$v)
|
||||
nop
|
||||
)
|
||||
)
|
30
tests/compiler/constant-assign.ts
Normal file
30
tests/compiler/constant-assign.ts
Normal file
@ -0,0 +1,30 @@
|
||||
// Expect error: TS2540
|
||||
// ^ TODO: Properly handle multiple
|
||||
|
||||
function localConst(a: i32): void {
|
||||
const b = a + 1;
|
||||
b = 3;
|
||||
}
|
||||
localConst(1);
|
||||
|
||||
function localConstInline(): void {
|
||||
const a = 1;
|
||||
a = 2;
|
||||
}
|
||||
localConstInline();
|
||||
|
||||
function localConstArray(a: i32): void {
|
||||
const b: i32[] = [ a ];
|
||||
b = [ 2 ];
|
||||
}
|
||||
localConstArray(1);
|
||||
|
||||
function localConstArrayInline(): void {
|
||||
const a: i32[] = [ 1 ];
|
||||
a = [ 2 ];
|
||||
}
|
||||
localConstArrayInline();
|
||||
|
||||
// globalConst
|
||||
const a = 1;
|
||||
a = 2;
|
0
tests/compiler/constant-assign.untouched.wat
Normal file
0
tests/compiler/constant-assign.untouched.wat
Normal file
Loading…
x
Reference in New Issue
Block a user