From e4f90a4bc8db41c47d62453ef5cfcc63cd2062f1 Mon Sep 17 00:00:00 2001 From: andreatp Date: Tue, 14 Oct 2025 12:58:55 +0100 Subject: [PATCH] [proposal] Extended Constant Expressions --- README.md | 1 + runtime-tests/pom.xml | 6 ++ .../chicory/runtime/ConstantEvaluators.java | 74 ++++++++++++++++--- .../com/dylibso/chicory/wasm/Validator.java | 64 ++++++++-------- 4 files changed, 104 insertions(+), 41 deletions(-) diff --git a/README.md b/README.md index eb8091e74..b602aa475 100644 --- a/README.md +++ b/README.md @@ -95,6 +95,7 @@ If you have an interest in working on any of these please reach out in Zulip! * [x] Compiler out of experimental * [x] Exception Handling * [x] Threads Support +* [x] Extended Constant Expressions * [ ] GC Support * in progress diff --git a/runtime-tests/pom.xml b/runtime-tests/pom.xml index 697475d5a..2cb7c6822 100644 --- a/runtime-tests/pom.xml +++ b/runtime-tests/pom.xml @@ -120,6 +120,9 @@ proposals/exception-handling/throw.wast proposals/exception-handling/throw_ref.wast proposals/exception-handling/try_table.wast + proposals/extended-const/data.wast + proposals/extended-const/elem.wast + proposals/extended-const/global.wast proposals/function-references/binary.wast proposals/function-references/br_on_non_null.wast proposals/function-references/br_on_null.wast @@ -375,6 +378,9 @@ proposals/exception-handling/throw.wast proposals/exception-handling/throw_ref.wast proposals/exception-handling/try_table.wast + proposals/extended-const/data.wast + proposals/extended-const/elem.wast + proposals/extended-const/global.wast proposals/function-references/binary.wast proposals/function-references/br_on_non_null.wast proposals/function-references/br_on_null.wast diff --git a/runtime/src/main/java/com/dylibso/chicory/runtime/ConstantEvaluators.java b/runtime/src/main/java/com/dylibso/chicory/runtime/ConstantEvaluators.java index d8c2ee351..d958912ac 100644 --- a/runtime/src/main/java/com/dylibso/chicory/runtime/ConstantEvaluators.java +++ b/runtime/src/main/java/com/dylibso/chicory/runtime/ConstantEvaluators.java @@ -2,9 +2,11 @@ import static com.dylibso.chicory.wasm.types.OpCode.GLOBAL_GET; +import com.dylibso.chicory.wasm.MalformedException; import com.dylibso.chicory.wasm.types.Instruction; import com.dylibso.chicory.wasm.types.ValType; import com.dylibso.chicory.wasm.types.Value; +import java.util.ArrayDeque; import java.util.Arrays; import java.util.List; @@ -16,44 +18,96 @@ public static long[] computeConstantValue(Instance instance, Instruction[] expr) } public static long[] computeConstantValue(Instance instance, List expr) { - long tos = -1L; + var stack = new ArrayDeque(); for (var instruction : expr) { switch (instruction.opcode()) { + case I32_ADD: + { + var x = (int) stack.pop()[0]; + var y = (int) stack.pop()[0]; + stack.push(new long[] {x + y}); + break; + } + case I32_SUB: + { + var x = (int) stack.pop()[0]; + var y = (int) stack.pop()[0]; + stack.push(new long[] {y - x}); + break; + } + case I32_MUL: + { + var x = (int) stack.pop()[0]; + var y = (int) stack.pop()[0]; + int res = x * y; + stack.push(new long[] {res}); + break; + } + case I64_ADD: + { + var x = stack.pop()[0]; + var y = stack.pop()[0]; + stack.push(new long[] {x + y}); + break; + } + case I64_SUB: + { + var x = stack.pop()[0]; + var y = stack.pop()[0]; + stack.push(new long[] {y - x}); + break; + } + case I64_MUL: + { + var x = stack.pop()[0]; + var y = stack.pop()[0]; + stack.push(new long[] {x * y}); + break; + } case V128_CONST: - return new long[] {instruction.operand(0), instruction.operand(1)}; + { + stack.push(new long[] {instruction.operand(0), instruction.operand(1)}); + break; + } case F32_CONST: case F64_CONST: case I32_CONST: case I64_CONST: case REF_FUNC: { - tos = instruction.operand(0); + stack.push(new long[] {instruction.operand(0)}); break; } case REF_NULL: { - tos = Value.REF_NULL_VALUE; + stack.push(new long[] {Value.REF_NULL_VALUE}); break; } case GLOBAL_GET: { var idx = (int) instruction.operand(0); if (instance.global(idx).getType().equals(ValType.V128)) { - return new long[] { - instance.global(idx).getValueLow(), - instance.global(idx).getValueHigh() - }; + stack.push( + new long[] { + instance.global(idx).getValueLow(), + instance.global(idx).getValueHigh() + }); } else { - return new long[] {instance.global(idx).getValueLow()}; + stack.push(new long[] {instance.global(idx).getValueLow()}); } + break; } case END: { break; } + default: + throw new MalformedException( + "Invalid instruction in constant value" + instruction); } } - return new long[] {tos}; + + return stack.pop(); } public static Instance computeConstantInstance(Instance instance, List expr) { diff --git a/wasm/src/main/java/com/dylibso/chicory/wasm/Validator.java b/wasm/src/main/java/com/dylibso/chicory/wasm/Validator.java index 7d7b1f050..134f22048 100644 --- a/wasm/src/main/java/com/dylibso/chicory/wasm/Validator.java +++ b/wasm/src/main/java/com/dylibso/chicory/wasm/Validator.java @@ -27,6 +27,7 @@ import com.dylibso.chicory.wasm.types.TagType; import com.dylibso.chicory.wasm.types.ValType; import com.dylibso.chicory.wasm.types.Value; +import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -526,9 +527,6 @@ void validateElements() { void validateGlobals() { for (Global g : module.globalSection().globals()) { validateConstantExpression(g.initInstructions(), g.valueType()); - if (g.mutabilityType() == MutabilityType.Const && g.initInstructions().size() > 1) { - throw new InvalidException("constant expression required"); - } } } @@ -536,47 +534,51 @@ private void validateConstantExpression( List expr, ValType expectedType) { validateValueType(expectedType); int allFuncCount = this.functionImports.size() + module.functionSection().functionCount(); - int constInstrCount = 0; + var valTypeStack = new ArrayDeque(); for (var instruction : expr) { - ValType exprType = null; - switch (instruction.opcode()) { case I32_CONST: - exprType = ValType.I32; - constInstrCount++; + valTypeStack.push(ValType.I32); + break; + case I32_ADD: + case I32_SUB: + case I32_MUL: + valTypeStack.pop(); + valTypeStack.pop(); + valTypeStack.push(ValType.I32); break; case I64_CONST: - exprType = ValType.I64; - constInstrCount++; + valTypeStack.push(ValType.I64); + break; + case I64_ADD: + case I64_SUB: + case I64_MUL: + valTypeStack.pop(); + valTypeStack.pop(); + valTypeStack.push(ValType.I64); break; case F32_CONST: - exprType = ValType.F32; - constInstrCount++; + valTypeStack.push(ValType.F32); break; case F64_CONST: - exprType = ValType.F64; - constInstrCount++; + valTypeStack.push(ValType.F64); break; case V128_CONST: - exprType = ValType.V128; - constInstrCount++; + valTypeStack.push(ValType.V128); break; case REF_NULL: { int operand = (int) instruction.operand(0); - exprType = valType(ValType.ID.RefNull, operand); - constInstrCount++; + valTypeStack.push(valType(ValType.ID.RefNull, operand)); break; } case REF_FUNC: { - constInstrCount++; int idx = (int) instruction.operand(0); - exprType = valType(ValType.ID.Ref, getFunctionType(idx)); + valTypeStack.push(valType(ValType.ID.Ref, getFunctionType(idx))); if (idx < 0 || idx > allFuncCount) { throw new InvalidException("unknown function " + idx); } - break; } case GLOBAL_GET: @@ -589,7 +591,7 @@ private void validateConstantExpression( "constant expression required, initializer expression" + " cannot reference a mutable global"); } - exprType = global.valueType(); + valTypeStack.push(global.valueType()); } else { throw new InvalidException( "unknown global " @@ -597,7 +599,6 @@ private void validateConstantExpression( + ", initializer expression can only reference" + " an imported global"); } - constInstrCount++; break; } case END: @@ -608,18 +609,19 @@ private void validateConstantExpression( + " encountered: " + instruction); } + } + if (valTypeStack.size() < 1) { + throw new InvalidException("type mismatch, no constant expressions found"); + } + if (valTypeStack.size() != 1) { + throw new InvalidException( + "type mismatch, values remaining on the stack after evaluation"); + } else { + var exprType = valTypeStack.pop(); if (exprType != null && !ValType.matches(exprType, expectedType)) { throw new InvalidException("type mismatch"); } - - // There must be at most one constant instruction. - if (constInstrCount > 1) { - throw new InvalidException("type mismatch, multiple constant expressions found"); - } - } - if (constInstrCount <= 0) { - throw new InvalidException("type mismatch, no constant expressions found"); } }