wasm gc: implement branching instructions and null check instruction

This commit is contained in:
Alexey Andreev 2024-09-13 19:46:11 +02:00
parent 5acb9a4707
commit 7029d3cff7
21 changed files with 567 additions and 12 deletions

View File

@ -120,10 +120,33 @@ public class DisassemblyCodeListener extends BaseDisassemblyListener implements
case BR_IF: case BR_IF:
writer.write("br_if"); writer.write("br_if");
break; 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(); 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 @Override
public void tableBranch(int[] depths, int[] targets, int defaultDepth, int defaultTarget) { public void tableBranch(int[] depths, int[] targets, int defaultDepth, int defaultTarget) {
writer.address(); writer.address();
@ -162,6 +185,9 @@ public class DisassemblyCodeListener extends BaseDisassemblyListener implements
case ARRAY_LENGTH: case ARRAY_LENGTH:
writer.write("array.length"); writer.write("array.length");
break; break;
case IS_NULL:
writer.write("ref.is_null");
break;
} }
writer.eol(); writer.eol();
} }

View File

@ -449,7 +449,7 @@ public abstract class BaseWasmGenerationVisitor implements StatementVisitor, Exp
protected abstract boolean isManagedCall(MethodReference method); 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); var block = new WasmBlock(false);
block.setLocation(location); block.setLocation(location);

View File

@ -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.WasmIntBinary;
import org.teavm.backend.wasm.model.expression.WasmIntBinaryOperation; import org.teavm.backend.wasm.model.expression.WasmIntBinaryOperation;
import org.teavm.backend.wasm.model.expression.WasmIntType; 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.WasmReferencesEqual;
import org.teavm.backend.wasm.model.expression.WasmReturn; import org.teavm.backend.wasm.model.expression.WasmReturn;
import org.teavm.backend.wasm.model.expression.WasmSetLocal; import org.teavm.backend.wasm.model.expression.WasmSetLocal;
@ -156,8 +156,7 @@ public class WasmGCSupertypeFunctionGenerator implements WasmGCSupertypeFunction
var itemExpression = getClassField(new WasmGetLocal(subtypeVar), itemOffset); var itemExpression = getClassField(new WasmGetLocal(subtypeVar), itemOffset);
body.add(new WasmSetLocal(subtypeVar, itemExpression)); body.add(new WasmSetLocal(subtypeVar, itemExpression));
var itemTest = new WasmConditional(new WasmReferencesEqual(new WasmGetLocal(subtypeVar), var itemTest = new WasmConditional(new WasmIsNull(new WasmGetLocal(subtypeVar)));
new WasmNullConstant(WasmType.Reference.STRUCT)));
itemTest.setType(WasmType.INT32); itemTest.setType(WasmType.INT32);
itemTest.getThenBlock().getBody().add(new WasmInt32Constant(0)); itemTest.getThenBlock().getBody().add(new WasmInt32Constant(0));

View File

@ -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.WasmIntType;
import org.teavm.backend.wasm.model.expression.WasmIntUnary; import org.teavm.backend.wasm.model.expression.WasmIntUnary;
import org.teavm.backend.wasm.model.expression.WasmIntUnaryOperation; 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.WasmNullConstant;
import org.teavm.backend.wasm.model.expression.WasmReferencesEqual; import org.teavm.backend.wasm.model.expression.WasmReferencesEqual;
import org.teavm.backend.wasm.model.expression.WasmSetGlobal; import org.teavm.backend.wasm.model.expression.WasmSetGlobal;
@ -232,7 +235,32 @@ public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor {
@Override @Override
protected WasmExpression genIsNull(WasmExpression value) { 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 @Override

View File

@ -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.WasmCallReference;
import org.teavm.backend.wasm.model.expression.WasmConditional; import org.teavm.backend.wasm.model.expression.WasmConditional;
import org.teavm.backend.wasm.model.expression.WasmGetLocal; import org.teavm.backend.wasm.model.expression.WasmGetLocal;
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.WasmReturn;
import org.teavm.backend.wasm.model.expression.WasmSetGlobal; import org.teavm.backend.wasm.model.expression.WasmSetGlobal;
import org.teavm.backend.wasm.model.expression.WasmStructGet; import org.teavm.backend.wasm.model.expression.WasmStructGet;
@ -50,8 +49,7 @@ public class ClassGenerators implements WasmGCCustomGenerator {
function.add(thisVar); function.add(thisVar);
function.add(otherClassVar); function.add(otherClassVar);
var conditional = new WasmConditional(new WasmReferencesEqual(new WasmGetLocal(otherClassVar), var conditional = new WasmConditional(new WasmIsNull(new WasmGetLocal(otherClassVar)));
new WasmNullConstant(WasmType.Reference.STRUCT)));
function.getBody().add(conditional); function.getBody().add(conditional);
var npe = new WasmCall(context.functions().forStaticMethod(new MethodReference(WasmGCSupport.class, "npe", var npe = new WasmCall(context.functions().forStaticMethod(new MethodReference(WasmGCSupport.class, "npe",
NullPointerException.class))); NullPointerException.class)));

View File

@ -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);
}
}

View File

@ -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
}

View File

@ -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 @Override
public void visit(WasmBreak expression) { public void visit(WasmBreak expression) {
if (expression.getResult() != null) { if (expression.getResult() != null) {
@ -85,6 +101,11 @@ public class WasmDefaultExpressionVisitor implements WasmExpressionVisitor {
public void visit(WasmNullConstant expression) { public void visit(WasmNullConstant expression) {
} }
@Override
public void visit(WasmIsNull expression) {
expression.getValue().acceptVisitor(this);
}
@Override @Override
public void visit(WasmGetLocal expression) { public void visit(WasmGetLocal expression) {
} }

View File

@ -20,6 +20,10 @@ public interface WasmExpressionVisitor {
void visit(WasmBranch expression); void visit(WasmBranch expression);
void visit(WasmNullBranch expression);
void visit(WasmCastBranch expression);
void visit(WasmBreak expression); void visit(WasmBreak expression);
void visit(WasmSwitch expression); void visit(WasmSwitch expression);
@ -40,6 +44,8 @@ public interface WasmExpressionVisitor {
void visit(WasmNullConstant expression); void visit(WasmNullConstant expression);
void visit(WasmIsNull expression);
void visit(WasmGetLocal expression); void visit(WasmGetLocal expression);
void visit(WasmSetLocal expression); void visit(WasmSetLocal expression);

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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
}

View File

@ -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 @Override
public void visit(WasmBreak expression) { public void visit(WasmBreak expression) {
if (expression.getResult() != null) { if (expression.getResult() != null) {
@ -109,6 +129,12 @@ public class WasmReplacingExpressionVisitor implements WasmExpressionVisitor {
public void visit(WasmNullConstant expression) { public void visit(WasmNullConstant expression) {
} }
@Override
public void visit(WasmIsNull expression) {
expression.getValue().acceptVisitor(this);
expression.setValue(mapper.apply(expression.getValue()));
}
@Override @Override
public void visit(WasmGetLocal expression) { public void visit(WasmGetLocal expression) {
} }

View File

@ -17,5 +17,7 @@ package org.teavm.backend.wasm.parser;
public enum BranchOpcode { public enum BranchOpcode {
BR, BR,
BR_IF BR_IF,
BR_ON_NULL,
BR_ON_NON_NULL
} }

View File

@ -54,6 +54,10 @@ public interface CodeListener {
default void branch(BranchOpcode opcode, int depth, int target) { 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) { default void tableBranch(int[] depths, int[] targets, int defaultDepth, int defaultTarget) {
} }

View File

@ -591,6 +591,9 @@ public class CodeParser extends BaseSectionParser {
case 0xD0: case 0xD0:
codeListener.nullConstant(reader.readHeapType(true)); codeListener.nullConstant(reader.readHeapType(true));
break; break;
case 0xD1:
codeListener.opcode(Opcode.IS_NULL);
break;
case 0xD2: case 0xD2:
codeListener.functionReference(readLEB()); codeListener.functionReference(readLEB());
@ -600,6 +603,13 @@ public class CodeParser extends BaseSectionParser {
codeListener.opcode(Opcode.REF_EQ); codeListener.opcode(Opcode.REF_EQ);
break; break;
case 0xD5:
parseBranch(BranchOpcode.BR_ON_NULL);
break;
case 0xD6:
parseBranch(BranchOpcode.BR_ON_NON_NULL);
break;
case 0xFB: case 0xFB:
return parseExtExpr2(); return parseExtExpr2();
case 0xFC: case 0xFC:
@ -702,6 +712,13 @@ public class CodeParser extends BaseSectionParser {
codeListener.cast(reader.readHeapType(true)); codeListener.cast(reader.readHeapType(true));
return true; return true;
case 24:
parseCastBranch(true);
return true;
case 25:
parseCastBranch(false);
return true;
case 28: case 28:
codeListener.int31Reference(); codeListener.int31Reference();
return true; return true;
@ -798,6 +815,15 @@ public class CodeParser extends BaseSectionParser {
codeListener.branch(opcode, depth, target.token); 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() { private void parseTableBranch() {
var count = readLEB(); var count = readLEB();
var depths = new int[count]; var depths = new int[count];

View File

@ -21,5 +21,6 @@ public enum Opcode {
RETURN, RETURN,
DROP, DROP,
REF_EQ, REF_EQ,
ARRAY_LENGTH ARRAY_LENGTH,
IS_NULL
} }

View File

@ -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.WasmCall;
import org.teavm.backend.wasm.model.expression.WasmCallReference; import org.teavm.backend.wasm.model.expression.WasmCallReference;
import org.teavm.backend.wasm.model.expression.WasmCast; 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.WasmConditional;
import org.teavm.backend.wasm.model.expression.WasmConversion; import org.teavm.backend.wasm.model.expression.WasmConversion;
import org.teavm.backend.wasm.model.expression.WasmCopy; 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.WasmInt64Constant;
import org.teavm.backend.wasm.model.expression.WasmIntBinary; import org.teavm.backend.wasm.model.expression.WasmIntBinary;
import org.teavm.backend.wasm.model.expression.WasmIntUnary; 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.WasmLoadFloat32;
import org.teavm.backend.wasm.model.expression.WasmLoadFloat64; import org.teavm.backend.wasm.model.expression.WasmLoadFloat64;
import org.teavm.backend.wasm.model.expression.WasmLoadInt32; import org.teavm.backend.wasm.model.expression.WasmLoadInt32;
import org.teavm.backend.wasm.model.expression.WasmLoadInt64; import org.teavm.backend.wasm.model.expression.WasmLoadInt64;
import org.teavm.backend.wasm.model.expression.WasmMemoryGrow; 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.WasmNullConstant;
import org.teavm.backend.wasm.model.expression.WasmReferencesEqual; import org.teavm.backend.wasm.model.expression.WasmReferencesEqual;
import org.teavm.backend.wasm.model.expression.WasmReturn; import org.teavm.backend.wasm.model.expression.WasmReturn;
@ -122,6 +125,18 @@ class WasmBinaryRenderingVisitor implements WasmExpressionVisitor {
register(expression.getTarget()); 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 @Override
public void visit(WasmBreak expression) { public void visit(WasmBreak expression) {
super.visit(expression); super.visit(expression);
@ -186,6 +201,55 @@ class WasmBinaryRenderingVisitor implements WasmExpressionVisitor {
popLocation(); 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 @Override
public void visit(WasmBreak expression) { public void visit(WasmBreak expression) {
pushLocation(expression); pushLocation(expression);
@ -304,6 +368,14 @@ class WasmBinaryRenderingVisitor implements WasmExpressionVisitor {
popLocation(); popLocation();
} }
@Override
public void visit(WasmIsNull expression) {
pushLocation(expression);
expression.getValue().acceptVisitor(this);
writer.writeByte(0xD1);
popLocation();
}
@Override @Override
public void visit(WasmGetLocal expression) { public void visit(WasmGetLocal expression) {
pushLocation(expression); pushLocation(expression);

View File

@ -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.WasmCall;
import org.teavm.backend.wasm.model.expression.WasmCallReference; import org.teavm.backend.wasm.model.expression.WasmCallReference;
import org.teavm.backend.wasm.model.expression.WasmCast; 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.WasmConditional;
import org.teavm.backend.wasm.model.expression.WasmConversion; import org.teavm.backend.wasm.model.expression.WasmConversion;
import org.teavm.backend.wasm.model.expression.WasmCopy; 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.WasmIntBinary;
import org.teavm.backend.wasm.model.expression.WasmIntType; import org.teavm.backend.wasm.model.expression.WasmIntType;
import org.teavm.backend.wasm.model.expression.WasmIntUnary; 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.WasmLoadFloat32;
import org.teavm.backend.wasm.model.expression.WasmLoadFloat64; import org.teavm.backend.wasm.model.expression.WasmLoadFloat64;
import org.teavm.backend.wasm.model.expression.WasmLoadInt32; import org.teavm.backend.wasm.model.expression.WasmLoadInt32;
import org.teavm.backend.wasm.model.expression.WasmLoadInt64; import org.teavm.backend.wasm.model.expression.WasmLoadInt64;
import org.teavm.backend.wasm.model.expression.WasmMemoryGrow; 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.WasmNullConstant;
import org.teavm.backend.wasm.model.expression.WasmReferencesEqual; import org.teavm.backend.wasm.model.expression.WasmReferencesEqual;
import org.teavm.backend.wasm.model.expression.WasmReturn; import org.teavm.backend.wasm.model.expression.WasmReturn;
@ -187,6 +190,16 @@ class WasmCRenderingVisitor implements WasmExpressionVisitor {
value = result; value = result;
} }
@Override
public void visit(WasmNullBranch expression) {
unsupported();
}
@Override
public void visit(WasmCastBranch expression) {
unsupported();
}
@Override @Override
public void visit(WasmBreak expression) { public void visit(WasmBreak expression) {
CExpression result = new CExpression(); CExpression result = new CExpression();
@ -359,6 +372,11 @@ class WasmCRenderingVisitor implements WasmExpressionVisitor {
value = CExpression.relocatable("/* can't produce ref.null */"); value = CExpression.relocatable("/* can't produce ref.null */");
} }
@Override
public void visit(WasmIsNull expression) {
unsupported();
}
@Override @Override
public void visit(WasmGetLocal expression) { public void visit(WasmGetLocal expression) {
value = new CExpression(getVariableName(expression.getLocal())); value = new CExpression(getVariableName(expression.getLocal()));

View File

@ -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.WasmCall;
import org.teavm.backend.wasm.model.expression.WasmCallReference; import org.teavm.backend.wasm.model.expression.WasmCallReference;
import org.teavm.backend.wasm.model.expression.WasmCast; 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.WasmConditional;
import org.teavm.backend.wasm.model.expression.WasmConversion; import org.teavm.backend.wasm.model.expression.WasmConversion;
import org.teavm.backend.wasm.model.expression.WasmCopy; 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.WasmIntType;
import org.teavm.backend.wasm.model.expression.WasmIntUnary; import org.teavm.backend.wasm.model.expression.WasmIntUnary;
import org.teavm.backend.wasm.model.expression.WasmIntUnaryOperation; 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.WasmLoadFloat32;
import org.teavm.backend.wasm.model.expression.WasmLoadFloat64; import org.teavm.backend.wasm.model.expression.WasmLoadFloat64;
import org.teavm.backend.wasm.model.expression.WasmLoadInt32; import org.teavm.backend.wasm.model.expression.WasmLoadInt32;
import org.teavm.backend.wasm.model.expression.WasmLoadInt64; import org.teavm.backend.wasm.model.expression.WasmLoadInt64;
import org.teavm.backend.wasm.model.expression.WasmMemoryGrow; 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.WasmNullConstant;
import org.teavm.backend.wasm.model.expression.WasmReferencesEqual; import org.teavm.backend.wasm.model.expression.WasmReferencesEqual;
import org.teavm.backend.wasm.model.expression.WasmReturn; import org.teavm.backend.wasm.model.expression.WasmReturn;
@ -108,6 +111,18 @@ class WasmRenderingVisitor implements WasmExpressionVisitor {
register(expression.getTarget()); 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 @Override
public void visit(WasmBreak expression) { public void visit(WasmBreak expression) {
super.visit(expression); super.visit(expression);
@ -219,6 +234,46 @@ class WasmRenderingVisitor implements WasmExpressionVisitor {
close(); 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 @Override
public void visit(WasmBreak expression) { public void visit(WasmBreak expression) {
String id = blockIdentifiers.get(expression.getTarget()); String id = blockIdentifiers.get(expression.getTarget());
@ -300,6 +355,11 @@ class WasmRenderingVisitor implements WasmExpressionVisitor {
open().append("ref.null " + type(expression.getType())).close(); open().append("ref.null " + type(expression.getType())).close();
} }
@Override
public void visit(WasmIsNull expression) {
open().append("ref.is_null").close();
}
@Override @Override
public void visit(WasmGetLocal expression) { public void visit(WasmGetLocal expression) {
open().append("local.get " + asString(expression.getLocal())).close(); open().append("local.get " + asString(expression.getLocal())).close();

View File

@ -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.WasmCall;
import org.teavm.backend.wasm.model.expression.WasmCallReference; import org.teavm.backend.wasm.model.expression.WasmCallReference;
import org.teavm.backend.wasm.model.expression.WasmCast; 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.WasmConditional;
import org.teavm.backend.wasm.model.expression.WasmConversion; import org.teavm.backend.wasm.model.expression.WasmConversion;
import org.teavm.backend.wasm.model.expression.WasmCopy; 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.WasmIntBinary;
import org.teavm.backend.wasm.model.expression.WasmIntType; import org.teavm.backend.wasm.model.expression.WasmIntType;
import org.teavm.backend.wasm.model.expression.WasmIntUnary; 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.WasmLoadFloat32;
import org.teavm.backend.wasm.model.expression.WasmLoadFloat64; import org.teavm.backend.wasm.model.expression.WasmLoadFloat64;
import org.teavm.backend.wasm.model.expression.WasmLoadInt32; import org.teavm.backend.wasm.model.expression.WasmLoadInt32;
import org.teavm.backend.wasm.model.expression.WasmLoadInt64; import org.teavm.backend.wasm.model.expression.WasmLoadInt64;
import org.teavm.backend.wasm.model.expression.WasmMemoryGrow; 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.WasmNullConstant;
import org.teavm.backend.wasm.model.expression.WasmReferencesEqual; import org.teavm.backend.wasm.model.expression.WasmReferencesEqual;
import org.teavm.backend.wasm.model.expression.WasmReturn; import org.teavm.backend.wasm.model.expression.WasmReturn;
@ -88,7 +91,28 @@ public class WasmTypeInference implements WasmExpressionVisitor {
@Override @Override
public void visit(WasmBranch expression) { 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 @Override
@ -186,6 +210,12 @@ public class WasmTypeInference implements WasmExpressionVisitor {
result = expression.type; result = expression.type;
} }
@Override
public void visit(WasmIsNull expression) {
result = WasmType.INT32;
}
@Override @Override
public void visit(WasmCall expression) { public void visit(WasmCall expression) {
var function = expression.getFunction(); var function = expression.getFunction();