From 7029d3cff705a9133714b24164fc51348e4fb790 Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Fri, 13 Sep 2024 19:46:11 +0200 Subject: [PATCH] wasm gc: implement branching instructions and null check instruction --- .../wasm/disasm/DisassemblyCodeListener.java | 26 ++++++ .../methods/BaseWasmGenerationVisitor.java | 2 +- .../WasmGCSupertypeFunctionGenerator.java | 5 +- .../gc/methods/WasmGCGenerationVisitor.java | 30 ++++++- .../wasm/generators/gc/ClassGenerators.java | 6 +- .../wasm/model/expression/WasmCastBranch.java | 89 +++++++++++++++++++ .../model/expression/WasmCastCondition.java | 21 +++++ .../WasmDefaultExpressionVisitor.java | 21 +++++ .../expression/WasmExpressionVisitor.java | 6 ++ .../wasm/model/expression/WasmIsNull.java | 39 ++++++++ .../wasm/model/expression/WasmNullBranch.java | 68 ++++++++++++++ .../model/expression/WasmNullCondition.java | 21 +++++ .../WasmReplacingExpressionVisitor.java | 26 ++++++ .../backend/wasm/parser/BranchOpcode.java | 4 +- .../backend/wasm/parser/CodeListener.java | 4 + .../teavm/backend/wasm/parser/CodeParser.java | 26 ++++++ .../org/teavm/backend/wasm/parser/Opcode.java | 3 +- .../render/WasmBinaryRenderingVisitor.java | 72 +++++++++++++++ .../wasm/render/WasmCRenderingVisitor.java | 18 ++++ .../wasm/render/WasmRenderingVisitor.java | 60 +++++++++++++ .../wasm/render/WasmTypeInference.java | 32 ++++++- 21 files changed, 567 insertions(+), 12 deletions(-) create mode 100644 core/src/main/java/org/teavm/backend/wasm/model/expression/WasmCastBranch.java create mode 100644 core/src/main/java/org/teavm/backend/wasm/model/expression/WasmCastCondition.java create mode 100644 core/src/main/java/org/teavm/backend/wasm/model/expression/WasmIsNull.java create mode 100644 core/src/main/java/org/teavm/backend/wasm/model/expression/WasmNullBranch.java create mode 100644 core/src/main/java/org/teavm/backend/wasm/model/expression/WasmNullCondition.java diff --git a/core/src/main/java/org/teavm/backend/wasm/disasm/DisassemblyCodeListener.java b/core/src/main/java/org/teavm/backend/wasm/disasm/DisassemblyCodeListener.java index ea65aee70..ecfff78c4 100644 --- a/core/src/main/java/org/teavm/backend/wasm/disasm/DisassemblyCodeListener.java +++ b/core/src/main/java/org/teavm/backend/wasm/disasm/DisassemblyCodeListener.java @@ -120,10 +120,33 @@ public class DisassemblyCodeListener extends BaseDisassemblyListener implements case BR_IF: writer.write("br_if"); break; + case BR_ON_NULL: + writer.write("br_on_null"); + break; + case BR_ON_NON_NULL: + writer.write("br_on_non_null"); + break; } writer.endLink().write(" $label_" + target).eol(); } + @Override + public void castBranch(boolean success, int depth, int target, WasmHollowType.Reference sourceType, + WasmHollowType.Reference targetType) { + writer.address().startLink("start" + target); + if (success) { + writer.write("br_if_cast"); + } else { + writer.write("br_if_cast_fail"); + } + writer.endLink().write(" $label_" + target); + writer.write(" "); + writeType(sourceType); + writer.write(" "); + writeType(sourceType); + writer.eol(); + } + @Override public void tableBranch(int[] depths, int[] targets, int defaultDepth, int defaultTarget) { writer.address(); @@ -162,6 +185,9 @@ public class DisassemblyCodeListener extends BaseDisassemblyListener implements case ARRAY_LENGTH: writer.write("array.length"); break; + case IS_NULL: + writer.write("ref.is_null"); + break; } writer.eol(); } diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/common/methods/BaseWasmGenerationVisitor.java b/core/src/main/java/org/teavm/backend/wasm/generate/common/methods/BaseWasmGenerationVisitor.java index 620c680fc..c5fd83a9f 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/common/methods/BaseWasmGenerationVisitor.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/common/methods/BaseWasmGenerationVisitor.java @@ -449,7 +449,7 @@ public abstract class BaseWasmGenerationVisitor implements StatementVisitor, Exp protected abstract boolean isManagedCall(MethodReference method); - private WasmExpression nullCheck(Expr value, TextLocation location) { + protected WasmExpression nullCheck(Expr value, TextLocation location) { var block = new WasmBlock(false); block.setLocation(location); diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCSupertypeFunctionGenerator.java b/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCSupertypeFunctionGenerator.java index 0abca345b..de9667143 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCSupertypeFunctionGenerator.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCSupertypeFunctionGenerator.java @@ -35,7 +35,7 @@ import org.teavm.backend.wasm.model.expression.WasmInt32Constant; import org.teavm.backend.wasm.model.expression.WasmIntBinary; import org.teavm.backend.wasm.model.expression.WasmIntBinaryOperation; import org.teavm.backend.wasm.model.expression.WasmIntType; -import org.teavm.backend.wasm.model.expression.WasmNullConstant; +import org.teavm.backend.wasm.model.expression.WasmIsNull; import org.teavm.backend.wasm.model.expression.WasmReferencesEqual; import org.teavm.backend.wasm.model.expression.WasmReturn; import org.teavm.backend.wasm.model.expression.WasmSetLocal; @@ -156,8 +156,7 @@ public class WasmGCSupertypeFunctionGenerator implements WasmGCSupertypeFunction var itemExpression = getClassField(new WasmGetLocal(subtypeVar), itemOffset); body.add(new WasmSetLocal(subtypeVar, itemExpression)); - var itemTest = new WasmConditional(new WasmReferencesEqual(new WasmGetLocal(subtypeVar), - new WasmNullConstant(WasmType.Reference.STRUCT))); + var itemTest = new WasmConditional(new WasmIsNull(new WasmGetLocal(subtypeVar))); itemTest.setType(WasmType.INT32); itemTest.getThenBlock().getBody().add(new WasmInt32Constant(0)); diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCGenerationVisitor.java b/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCGenerationVisitor.java index 098d8a043..3ae039088 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCGenerationVisitor.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCGenerationVisitor.java @@ -57,6 +57,9 @@ import org.teavm.backend.wasm.model.expression.WasmGetLocal; import org.teavm.backend.wasm.model.expression.WasmIntType; import org.teavm.backend.wasm.model.expression.WasmIntUnary; import org.teavm.backend.wasm.model.expression.WasmIntUnaryOperation; +import org.teavm.backend.wasm.model.expression.WasmIsNull; +import org.teavm.backend.wasm.model.expression.WasmNullBranch; +import org.teavm.backend.wasm.model.expression.WasmNullCondition; import org.teavm.backend.wasm.model.expression.WasmNullConstant; import org.teavm.backend.wasm.model.expression.WasmReferencesEqual; import org.teavm.backend.wasm.model.expression.WasmSetGlobal; @@ -232,7 +235,32 @@ public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor { @Override protected WasmExpression genIsNull(WasmExpression value) { - return new WasmReferencesEqual(value, new WasmNullConstant(WasmType.Reference.STRUCT)); + return new WasmIsNull(value); + } + + protected WasmExpression nullCheck(Expr value, TextLocation location) { + var block = new WasmBlock(false); + block.setLocation(location); + + accept(value); + if (result instanceof WasmUnreachable) { + return result; + } + result.acceptVisitor(typeInference); + block.setType(typeInference.getResult()); + var cachedValue = exprCache.create(result, typeInference.getResult(), location, block.getBody()); + + var check = new WasmNullBranch(WasmNullCondition.NOT_NULL, cachedValue.expr(), block); + check.setResult(cachedValue.expr()); + block.getBody().add(new WasmDrop(check)); + + var callSiteId = generateCallSiteId(location); + callSiteId.generateRegister(block.getBody(), location); + generateThrowNPE(location, block.getBody()); + callSiteId.generateThrow(block.getBody(), location); + + cachedValue.release(); + return block; } @Override diff --git a/core/src/main/java/org/teavm/backend/wasm/generators/gc/ClassGenerators.java b/core/src/main/java/org/teavm/backend/wasm/generators/gc/ClassGenerators.java index 9a95583cd..ebcd3beca 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generators/gc/ClassGenerators.java +++ b/core/src/main/java/org/teavm/backend/wasm/generators/gc/ClassGenerators.java @@ -22,8 +22,7 @@ import org.teavm.backend.wasm.model.expression.WasmCall; import org.teavm.backend.wasm.model.expression.WasmCallReference; import org.teavm.backend.wasm.model.expression.WasmConditional; import org.teavm.backend.wasm.model.expression.WasmGetLocal; -import org.teavm.backend.wasm.model.expression.WasmNullConstant; -import org.teavm.backend.wasm.model.expression.WasmReferencesEqual; +import org.teavm.backend.wasm.model.expression.WasmIsNull; import org.teavm.backend.wasm.model.expression.WasmReturn; import org.teavm.backend.wasm.model.expression.WasmSetGlobal; import org.teavm.backend.wasm.model.expression.WasmStructGet; @@ -50,8 +49,7 @@ public class ClassGenerators implements WasmGCCustomGenerator { function.add(thisVar); function.add(otherClassVar); - var conditional = new WasmConditional(new WasmReferencesEqual(new WasmGetLocal(otherClassVar), - new WasmNullConstant(WasmType.Reference.STRUCT))); + var conditional = new WasmConditional(new WasmIsNull(new WasmGetLocal(otherClassVar))); function.getBody().add(conditional); var npe = new WasmCall(context.functions().forStaticMethod(new MethodReference(WasmGCSupport.class, "npe", NullPointerException.class))); diff --git a/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmCastBranch.java b/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmCastBranch.java new file mode 100644 index 000000000..91d40c108 --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmCastBranch.java @@ -0,0 +1,89 @@ +/* + * Copyright 2024 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.backend.wasm.model.expression; + +import java.util.Objects; +import org.teavm.backend.wasm.model.WasmType; + +public class WasmCastBranch extends WasmExpression { + private WasmCastCondition condition; + private WasmExpression value; + private WasmType.Reference sourceType; + private WasmType.Reference type; + private WasmBlock target; + private WasmExpression result; + + public WasmCastBranch(WasmCastCondition condition, WasmExpression value, WasmType.Reference sourceType, + WasmType.Reference type, WasmBlock target) { + this.condition = Objects.requireNonNull(condition); + this.value = Objects.requireNonNull(value); + this.type = Objects.requireNonNull(type); + this.target = Objects.requireNonNull(target); + } + + public WasmCastCondition getCondition() { + return condition; + } + + public void setCondition(WasmCastCondition condition) { + this.condition = Objects.requireNonNull(condition); + } + + public WasmExpression getValue() { + return value; + } + + public void setValue(WasmExpression value) { + this.value = Objects.requireNonNull(value); + } + + public WasmType.Reference getSourceType() { + return sourceType; + } + + public void setSourceType(WasmType.Reference sourceType) { + this.sourceType = Objects.requireNonNull(sourceType); + } + + public WasmType.Reference getType() { + return type; + } + + public void setType(WasmType.Reference type) { + this.type = Objects.requireNonNull(type); + } + + public WasmBlock getTarget() { + return target; + } + + public void setTarget(WasmBlock target) { + this.target = Objects.requireNonNull(target); + } + + public WasmExpression getResult() { + return result; + } + + public void setResult(WasmExpression result) { + this.result = result; + } + + @Override + public void acceptVisitor(WasmExpressionVisitor visitor) { + visitor.visit(this); + } +} diff --git a/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmCastCondition.java b/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmCastCondition.java new file mode 100644 index 000000000..757517d6f --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmCastCondition.java @@ -0,0 +1,21 @@ +/* + * Copyright 2024 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.backend.wasm.model.expression; + +public enum WasmCastCondition { + SUCCESS, + FAILURE +} diff --git a/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmDefaultExpressionVisitor.java b/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmDefaultExpressionVisitor.java index 226ccbb67..2e14c2b2e 100644 --- a/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmDefaultExpressionVisitor.java +++ b/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmDefaultExpressionVisitor.java @@ -31,6 +31,22 @@ public class WasmDefaultExpressionVisitor implements WasmExpressionVisitor { } } + @Override + public void visit(WasmNullBranch expression) { + expression.getValue().acceptVisitor(this); + if (expression.getResult() != null) { + expression.getResult().acceptVisitor(this); + } + } + + @Override + public void visit(WasmCastBranch expression) { + expression.getValue().acceptVisitor(this); + if (expression.getResult() != null) { + expression.getResult().acceptVisitor(this); + } + } + @Override public void visit(WasmBreak expression) { if (expression.getResult() != null) { @@ -85,6 +101,11 @@ public class WasmDefaultExpressionVisitor implements WasmExpressionVisitor { public void visit(WasmNullConstant expression) { } + @Override + public void visit(WasmIsNull expression) { + expression.getValue().acceptVisitor(this); + } + @Override public void visit(WasmGetLocal expression) { } diff --git a/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmExpressionVisitor.java b/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmExpressionVisitor.java index da7894653..e1e512a75 100644 --- a/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmExpressionVisitor.java +++ b/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmExpressionVisitor.java @@ -20,6 +20,10 @@ public interface WasmExpressionVisitor { void visit(WasmBranch expression); + void visit(WasmNullBranch expression); + + void visit(WasmCastBranch expression); + void visit(WasmBreak expression); void visit(WasmSwitch expression); @@ -40,6 +44,8 @@ public interface WasmExpressionVisitor { void visit(WasmNullConstant expression); + void visit(WasmIsNull expression); + void visit(WasmGetLocal expression); void visit(WasmSetLocal expression); diff --git a/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmIsNull.java b/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmIsNull.java new file mode 100644 index 000000000..326da64f3 --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmIsNull.java @@ -0,0 +1,39 @@ +/* + * Copyright 2024 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.backend.wasm.model.expression; + +import java.util.Objects; + +public class WasmIsNull extends WasmExpression { + private WasmExpression value; + + public WasmIsNull(WasmExpression value) { + this.value = Objects.requireNonNull(value); + } + + public WasmExpression getValue() { + return value; + } + + public void setValue(WasmExpression value) { + this.value = value; + } + + @Override + public void acceptVisitor(WasmExpressionVisitor visitor) { + visitor.visit(this); + } +} diff --git a/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmNullBranch.java b/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmNullBranch.java new file mode 100644 index 000000000..a8c9f05fe --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmNullBranch.java @@ -0,0 +1,68 @@ +/* + * Copyright 2024 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.backend.wasm.model.expression; + +import java.util.Objects; + +public class WasmNullBranch extends WasmExpression { + private WasmNullCondition condition; + private WasmExpression value; + private WasmBlock target; + private WasmExpression result; + + public WasmNullBranch(WasmNullCondition condition, WasmExpression value, WasmBlock target) { + this.condition = Objects.requireNonNull(condition); + this.value = Objects.requireNonNull(value); + this.target = Objects.requireNonNull(target); + } + + public WasmNullCondition getCondition() { + return condition; + } + + public void setCondition(WasmNullCondition condition) { + this.condition = Objects.requireNonNull(condition); + } + + public WasmExpression getValue() { + return value; + } + + public void setValue(WasmExpression value) { + this.value = Objects.requireNonNull(value); + } + + public WasmBlock getTarget() { + return target; + } + + public void setTarget(WasmBlock target) { + this.target = Objects.requireNonNull(target); + } + + public WasmExpression getResult() { + return result; + } + + public void setResult(WasmExpression result) { + this.result = result; + } + + @Override + public void acceptVisitor(WasmExpressionVisitor visitor) { + visitor.visit(this); + } +} diff --git a/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmNullCondition.java b/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmNullCondition.java new file mode 100644 index 000000000..6ba8b82de --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmNullCondition.java @@ -0,0 +1,21 @@ +/* + * Copyright 2024 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.backend.wasm.model.expression; + +public enum WasmNullCondition { + NULL, + NOT_NULL +} diff --git a/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmReplacingExpressionVisitor.java b/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmReplacingExpressionVisitor.java index 021750804..679c98d84 100644 --- a/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmReplacingExpressionVisitor.java +++ b/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmReplacingExpressionVisitor.java @@ -55,6 +55,26 @@ public class WasmReplacingExpressionVisitor implements WasmExpressionVisitor { } } + @Override + public void visit(WasmNullBranch expression) { + expression.getValue().acceptVisitor(this); + expression.setValue(mapper.apply(expression.getValue())); + if (expression.getResult() != null) { + expression.getResult().acceptVisitor(this); + expression.setResult(mapper.apply(expression.getResult())); + } + } + + @Override + public void visit(WasmCastBranch expression) { + expression.getValue().acceptVisitor(this); + expression.setValue(mapper.apply(expression.getValue())); + if (expression.getResult() != null) { + expression.getResult().acceptVisitor(this); + expression.setResult(mapper.apply(expression.getResult())); + } + } + @Override public void visit(WasmBreak expression) { if (expression.getResult() != null) { @@ -109,6 +129,12 @@ public class WasmReplacingExpressionVisitor implements WasmExpressionVisitor { public void visit(WasmNullConstant expression) { } + @Override + public void visit(WasmIsNull expression) { + expression.getValue().acceptVisitor(this); + expression.setValue(mapper.apply(expression.getValue())); + } + @Override public void visit(WasmGetLocal expression) { } diff --git a/core/src/main/java/org/teavm/backend/wasm/parser/BranchOpcode.java b/core/src/main/java/org/teavm/backend/wasm/parser/BranchOpcode.java index 8dcf6e60a..40e702249 100644 --- a/core/src/main/java/org/teavm/backend/wasm/parser/BranchOpcode.java +++ b/core/src/main/java/org/teavm/backend/wasm/parser/BranchOpcode.java @@ -17,5 +17,7 @@ package org.teavm.backend.wasm.parser; public enum BranchOpcode { BR, - BR_IF + BR_IF, + BR_ON_NULL, + BR_ON_NON_NULL } diff --git a/core/src/main/java/org/teavm/backend/wasm/parser/CodeListener.java b/core/src/main/java/org/teavm/backend/wasm/parser/CodeListener.java index 03ebfbc16..7876c43fc 100644 --- a/core/src/main/java/org/teavm/backend/wasm/parser/CodeListener.java +++ b/core/src/main/java/org/teavm/backend/wasm/parser/CodeListener.java @@ -54,6 +54,10 @@ public interface CodeListener { default void branch(BranchOpcode opcode, int depth, int target) { } + default void castBranch(boolean success, int depth, int target, WasmHollowType.Reference sourceType, + WasmHollowType.Reference targetType) { + } + default void tableBranch(int[] depths, int[] targets, int defaultDepth, int defaultTarget) { } diff --git a/core/src/main/java/org/teavm/backend/wasm/parser/CodeParser.java b/core/src/main/java/org/teavm/backend/wasm/parser/CodeParser.java index a121193a8..70c62deac 100644 --- a/core/src/main/java/org/teavm/backend/wasm/parser/CodeParser.java +++ b/core/src/main/java/org/teavm/backend/wasm/parser/CodeParser.java @@ -591,6 +591,9 @@ public class CodeParser extends BaseSectionParser { case 0xD0: codeListener.nullConstant(reader.readHeapType(true)); break; + case 0xD1: + codeListener.opcode(Opcode.IS_NULL); + break; case 0xD2: codeListener.functionReference(readLEB()); @@ -600,6 +603,13 @@ public class CodeParser extends BaseSectionParser { codeListener.opcode(Opcode.REF_EQ); break; + case 0xD5: + parseBranch(BranchOpcode.BR_ON_NULL); + break; + case 0xD6: + parseBranch(BranchOpcode.BR_ON_NON_NULL); + break; + case 0xFB: return parseExtExpr2(); case 0xFC: @@ -702,6 +712,13 @@ public class CodeParser extends BaseSectionParser { codeListener.cast(reader.readHeapType(true)); return true; + case 24: + parseCastBranch(true); + return true; + case 25: + parseCastBranch(false); + return true; + case 28: codeListener.int31Reference(); return true; @@ -798,6 +815,15 @@ public class CodeParser extends BaseSectionParser { codeListener.branch(opcode, depth, target.token); } + private void parseCastBranch(boolean success) { + var depth = readLEB(); + var target = blockStack.get(blockStack.size() - depth - 1); + var flags = reader.data[reader.ptr++]; + var sourceType = reader.readHeapType((flags & 1) != 0); + var targetType = reader.readHeapType((flags & 2) != 0); + codeListener.castBranch(success, depth, target.token, sourceType, targetType); + } + private void parseTableBranch() { var count = readLEB(); var depths = new int[count]; diff --git a/core/src/main/java/org/teavm/backend/wasm/parser/Opcode.java b/core/src/main/java/org/teavm/backend/wasm/parser/Opcode.java index 3ea33378c..4cca9d0d2 100644 --- a/core/src/main/java/org/teavm/backend/wasm/parser/Opcode.java +++ b/core/src/main/java/org/teavm/backend/wasm/parser/Opcode.java @@ -21,5 +21,6 @@ public enum Opcode { RETURN, DROP, REF_EQ, - ARRAY_LENGTH + ARRAY_LENGTH, + IS_NULL } diff --git a/core/src/main/java/org/teavm/backend/wasm/render/WasmBinaryRenderingVisitor.java b/core/src/main/java/org/teavm/backend/wasm/render/WasmBinaryRenderingVisitor.java index 53b0e4845..f40603583 100644 --- a/core/src/main/java/org/teavm/backend/wasm/render/WasmBinaryRenderingVisitor.java +++ b/core/src/main/java/org/teavm/backend/wasm/render/WasmBinaryRenderingVisitor.java @@ -39,6 +39,7 @@ import org.teavm.backend.wasm.model.expression.WasmBreak; import org.teavm.backend.wasm.model.expression.WasmCall; import org.teavm.backend.wasm.model.expression.WasmCallReference; import org.teavm.backend.wasm.model.expression.WasmCast; +import org.teavm.backend.wasm.model.expression.WasmCastBranch; import org.teavm.backend.wasm.model.expression.WasmConditional; import org.teavm.backend.wasm.model.expression.WasmConversion; import org.teavm.backend.wasm.model.expression.WasmCopy; @@ -61,11 +62,13 @@ import org.teavm.backend.wasm.model.expression.WasmInt32Constant; import org.teavm.backend.wasm.model.expression.WasmInt64Constant; import org.teavm.backend.wasm.model.expression.WasmIntBinary; import org.teavm.backend.wasm.model.expression.WasmIntUnary; +import org.teavm.backend.wasm.model.expression.WasmIsNull; import org.teavm.backend.wasm.model.expression.WasmLoadFloat32; import org.teavm.backend.wasm.model.expression.WasmLoadFloat64; import org.teavm.backend.wasm.model.expression.WasmLoadInt32; import org.teavm.backend.wasm.model.expression.WasmLoadInt64; import org.teavm.backend.wasm.model.expression.WasmMemoryGrow; +import org.teavm.backend.wasm.model.expression.WasmNullBranch; import org.teavm.backend.wasm.model.expression.WasmNullConstant; import org.teavm.backend.wasm.model.expression.WasmReferencesEqual; import org.teavm.backend.wasm.model.expression.WasmReturn; @@ -122,6 +125,18 @@ class WasmBinaryRenderingVisitor implements WasmExpressionVisitor { register(expression.getTarget()); } + @Override + public void visit(WasmNullBranch expression) { + super.visit(expression); + register(expression.getTarget()); + } + + @Override + public void visit(WasmCastBranch expression) { + super.visit(expression); + register(expression.getTarget()); + } + @Override public void visit(WasmBreak expression) { super.visit(expression); @@ -186,6 +201,55 @@ class WasmBinaryRenderingVisitor implements WasmExpressionVisitor { popLocation(); } + @Override + public void visit(WasmNullBranch expression) { + pushLocation(expression); + if (expression.getResult() != null) { + expression.getResult().acceptVisitor(this); + } + expression.getValue().acceptVisitor(this); + switch (expression.getCondition()) { + case NULL: + writer.writeByte(0xD5); + break; + case NOT_NULL: + writer.writeByte(0xD6); + break; + } + writeLabel(expression.getTarget()); + popLocation(); + } + + @Override + public void visit(WasmCastBranch expression) { + pushLocation(expression); + if (expression.getResult() != null) { + expression.getResult().acceptVisitor(this); + } + expression.getValue().acceptVisitor(this); + writer.writeByte(0xFB); + switch (expression.getCondition()) { + case SUCCESS: + writer.writeByte(24); + break; + case FAILURE: + writer.writeByte(25); + break; + } + writeLabel(expression.getTarget()); + var flags = 0; + if (expression.getSourceType().isNullable()) { + flags |= 1; + } + if (expression.getType().isNullable()) { + flags |= 2; + } + writer.writeByte(flags); + writer.writeHeapType(expression.getSourceType(), module); + writer.writeHeapType(expression.getType(), module); + popLocation(); + } + @Override public void visit(WasmBreak expression) { pushLocation(expression); @@ -304,6 +368,14 @@ class WasmBinaryRenderingVisitor implements WasmExpressionVisitor { popLocation(); } + @Override + public void visit(WasmIsNull expression) { + pushLocation(expression); + expression.getValue().acceptVisitor(this); + writer.writeByte(0xD1); + popLocation(); + } + @Override public void visit(WasmGetLocal expression) { pushLocation(expression); diff --git a/core/src/main/java/org/teavm/backend/wasm/render/WasmCRenderingVisitor.java b/core/src/main/java/org/teavm/backend/wasm/render/WasmCRenderingVisitor.java index 6ffde3ba2..5e1a7a064 100644 --- a/core/src/main/java/org/teavm/backend/wasm/render/WasmCRenderingVisitor.java +++ b/core/src/main/java/org/teavm/backend/wasm/render/WasmCRenderingVisitor.java @@ -39,6 +39,7 @@ import org.teavm.backend.wasm.model.expression.WasmBreak; import org.teavm.backend.wasm.model.expression.WasmCall; import org.teavm.backend.wasm.model.expression.WasmCallReference; import org.teavm.backend.wasm.model.expression.WasmCast; +import org.teavm.backend.wasm.model.expression.WasmCastBranch; import org.teavm.backend.wasm.model.expression.WasmConditional; import org.teavm.backend.wasm.model.expression.WasmConversion; import org.teavm.backend.wasm.model.expression.WasmCopy; @@ -62,11 +63,13 @@ import org.teavm.backend.wasm.model.expression.WasmInt64Constant; import org.teavm.backend.wasm.model.expression.WasmIntBinary; import org.teavm.backend.wasm.model.expression.WasmIntType; import org.teavm.backend.wasm.model.expression.WasmIntUnary; +import org.teavm.backend.wasm.model.expression.WasmIsNull; import org.teavm.backend.wasm.model.expression.WasmLoadFloat32; import org.teavm.backend.wasm.model.expression.WasmLoadFloat64; import org.teavm.backend.wasm.model.expression.WasmLoadInt32; import org.teavm.backend.wasm.model.expression.WasmLoadInt64; import org.teavm.backend.wasm.model.expression.WasmMemoryGrow; +import org.teavm.backend.wasm.model.expression.WasmNullBranch; import org.teavm.backend.wasm.model.expression.WasmNullConstant; import org.teavm.backend.wasm.model.expression.WasmReferencesEqual; import org.teavm.backend.wasm.model.expression.WasmReturn; @@ -187,6 +190,16 @@ class WasmCRenderingVisitor implements WasmExpressionVisitor { value = result; } + @Override + public void visit(WasmNullBranch expression) { + unsupported(); + } + + @Override + public void visit(WasmCastBranch expression) { + unsupported(); + } + @Override public void visit(WasmBreak expression) { CExpression result = new CExpression(); @@ -359,6 +372,11 @@ class WasmCRenderingVisitor implements WasmExpressionVisitor { value = CExpression.relocatable("/* can't produce ref.null */"); } + @Override + public void visit(WasmIsNull expression) { + unsupported(); + } + @Override public void visit(WasmGetLocal expression) { value = new CExpression(getVariableName(expression.getLocal())); diff --git a/core/src/main/java/org/teavm/backend/wasm/render/WasmRenderingVisitor.java b/core/src/main/java/org/teavm/backend/wasm/render/WasmRenderingVisitor.java index d0e341d4a..d31fb0de8 100644 --- a/core/src/main/java/org/teavm/backend/wasm/render/WasmRenderingVisitor.java +++ b/core/src/main/java/org/teavm/backend/wasm/render/WasmRenderingVisitor.java @@ -35,6 +35,7 @@ import org.teavm.backend.wasm.model.expression.WasmBreak; import org.teavm.backend.wasm.model.expression.WasmCall; import org.teavm.backend.wasm.model.expression.WasmCallReference; import org.teavm.backend.wasm.model.expression.WasmCast; +import org.teavm.backend.wasm.model.expression.WasmCastBranch; import org.teavm.backend.wasm.model.expression.WasmConditional; import org.teavm.backend.wasm.model.expression.WasmConversion; import org.teavm.backend.wasm.model.expression.WasmCopy; @@ -63,11 +64,13 @@ import org.teavm.backend.wasm.model.expression.WasmIntBinaryOperation; import org.teavm.backend.wasm.model.expression.WasmIntType; import org.teavm.backend.wasm.model.expression.WasmIntUnary; import org.teavm.backend.wasm.model.expression.WasmIntUnaryOperation; +import org.teavm.backend.wasm.model.expression.WasmIsNull; import org.teavm.backend.wasm.model.expression.WasmLoadFloat32; import org.teavm.backend.wasm.model.expression.WasmLoadFloat64; import org.teavm.backend.wasm.model.expression.WasmLoadInt32; import org.teavm.backend.wasm.model.expression.WasmLoadInt64; import org.teavm.backend.wasm.model.expression.WasmMemoryGrow; +import org.teavm.backend.wasm.model.expression.WasmNullBranch; import org.teavm.backend.wasm.model.expression.WasmNullConstant; import org.teavm.backend.wasm.model.expression.WasmReferencesEqual; import org.teavm.backend.wasm.model.expression.WasmReturn; @@ -108,6 +111,18 @@ class WasmRenderingVisitor implements WasmExpressionVisitor { register(expression.getTarget()); } + @Override + public void visit(WasmCastBranch expression) { + super.visit(expression); + register(expression.getTarget()); + } + + @Override + public void visit(WasmNullBranch expression) { + super.visit(expression); + register(expression.getTarget()); + } + @Override public void visit(WasmBreak expression) { super.visit(expression); @@ -219,6 +234,46 @@ class WasmRenderingVisitor implements WasmExpressionVisitor { close(); } + @Override + public void visit(WasmNullBranch expression) { + String id = blockIdentifiers.get(expression.getTarget()); + open(); + switch (expression.getCondition()) { + case NULL: + append("br_on_null"); + break; + case NOT_NULL: + append("br_on_non_null"); + break; + } + append(" $" + id); + if (expression.getResult() != null) { + line(expression.getResult()); + } + close(); + } + + @Override + public void visit(WasmCastBranch expression) { + String id = blockIdentifiers.get(expression.getTarget()); + open(); + switch (expression.getCondition()) { + case SUCCESS: + append("br_on_cast"); + break; + case FAILURE: + append("br_on_cast_fail"); + break; + } + append(" $").append(id); + append(" ").append(type(expression.getSourceType())); + append(" ").append(type(expression.getType())); + if (expression.getResult() != null) { + line(expression.getResult()); + } + close(); + } + @Override public void visit(WasmBreak expression) { String id = blockIdentifiers.get(expression.getTarget()); @@ -300,6 +355,11 @@ class WasmRenderingVisitor implements WasmExpressionVisitor { open().append("ref.null " + type(expression.getType())).close(); } + @Override + public void visit(WasmIsNull expression) { + open().append("ref.is_null").close(); + } + @Override public void visit(WasmGetLocal expression) { open().append("local.get " + asString(expression.getLocal())).close(); diff --git a/core/src/main/java/org/teavm/backend/wasm/render/WasmTypeInference.java b/core/src/main/java/org/teavm/backend/wasm/render/WasmTypeInference.java index ebf6903b5..2544f0318 100644 --- a/core/src/main/java/org/teavm/backend/wasm/render/WasmTypeInference.java +++ b/core/src/main/java/org/teavm/backend/wasm/render/WasmTypeInference.java @@ -28,6 +28,7 @@ import org.teavm.backend.wasm.model.expression.WasmBreak; import org.teavm.backend.wasm.model.expression.WasmCall; import org.teavm.backend.wasm.model.expression.WasmCallReference; import org.teavm.backend.wasm.model.expression.WasmCast; +import org.teavm.backend.wasm.model.expression.WasmCastBranch; import org.teavm.backend.wasm.model.expression.WasmConditional; import org.teavm.backend.wasm.model.expression.WasmConversion; import org.teavm.backend.wasm.model.expression.WasmCopy; @@ -50,11 +51,13 @@ import org.teavm.backend.wasm.model.expression.WasmInt64Constant; import org.teavm.backend.wasm.model.expression.WasmIntBinary; import org.teavm.backend.wasm.model.expression.WasmIntType; import org.teavm.backend.wasm.model.expression.WasmIntUnary; +import org.teavm.backend.wasm.model.expression.WasmIsNull; import org.teavm.backend.wasm.model.expression.WasmLoadFloat32; import org.teavm.backend.wasm.model.expression.WasmLoadFloat64; import org.teavm.backend.wasm.model.expression.WasmLoadInt32; import org.teavm.backend.wasm.model.expression.WasmLoadInt64; import org.teavm.backend.wasm.model.expression.WasmMemoryGrow; +import org.teavm.backend.wasm.model.expression.WasmNullBranch; import org.teavm.backend.wasm.model.expression.WasmNullConstant; import org.teavm.backend.wasm.model.expression.WasmReferencesEqual; import org.teavm.backend.wasm.model.expression.WasmReturn; @@ -88,7 +91,28 @@ public class WasmTypeInference implements WasmExpressionVisitor { @Override public void visit(WasmBranch expression) { - result = null; + if (expression.getResult() != null) { + expression.acceptVisitor(this); + } + } + + @Override + public void visit(WasmNullBranch expression) { + if (expression.getResult() != null) { + expression.acceptVisitor(this); + } + } + + @Override + public void visit(WasmCastBranch expression) { + switch (expression.getCondition()) { + case SUCCESS: + result = expression.getSourceType(); + break; + case FAILURE: + result = expression.getType(); + break; + } } @Override @@ -186,6 +210,12 @@ public class WasmTypeInference implements WasmExpressionVisitor { result = expression.type; } + + @Override + public void visit(WasmIsNull expression) { + result = WasmType.INT32; + } + @Override public void visit(WasmCall expression) { var function = expression.getFunction();