From 078cd8b208cbb6c191418f77333360e500f73bb0 Mon Sep 17 00:00:00 2001 From: Su Yihan Date: Mon, 29 Jan 2024 17:34:12 +0800 Subject: [PATCH] support non-constant index get in tuple Signed-off-by: Su Yihan --- lib/builtin/builtin_name.ts | 1 + src/backend/binaryen/glue/packType.ts | 2 + src/backend/binaryen/glue/transform.ts | 15 ++ src/backend/binaryen/lib/init_builtin_api.ts | 256 +++++++++++++++++++ src/backend/binaryen/utils.ts | 2 +- src/backend/binaryen/wasm_expr_gen.ts | 100 ++++++-- 6 files changed, 352 insertions(+), 24 deletions(-) diff --git a/lib/builtin/builtin_name.ts b/lib/builtin/builtin_name.ts index 4892c9ea..651f3018 100644 --- a/lib/builtin/builtin_name.ts +++ b/lib/builtin/builtin_name.ts @@ -59,6 +59,7 @@ export namespace BuiltinNames { export const findPropertyFlagAndIndex = 'find_property_flag_and_index'; export const findPropertyType = 'find_property_type'; export const getInfcProperty = 'get_infc_property'; + export const getTupleField = 'get_tuple_field'; // builtin globals export const builtinTypeManglePrefix = 'lib/builtin/lib.type.d'; diff --git a/src/backend/binaryen/glue/packType.ts b/src/backend/binaryen/glue/packType.ts index 2227d26c..1c456120 100644 --- a/src/backend/binaryen/glue/packType.ts +++ b/src/backend/binaryen/glue/packType.ts @@ -18,12 +18,14 @@ import { arrayBufferType, dataViewType, numberArrayStructType, + i32ArrayType, } from './transform.js'; import { typeInfo } from './utils.js'; export const i8ArrayTypeInfo: typeInfo = i8ArrayType; export const stringTypeInfo: typeInfo = stringType; export const numberArrayTypeInfo = numberArrayType; +export const i32ArrayTypeInfo = i32ArrayType; export const stringArrayTypeInfo = stringArrayType; export const stringArrayStructTypeInfo = stringArrayStructType; export const stringrefArrayTypeInfo = stringrefArrayType; diff --git a/src/backend/binaryen/glue/transform.ts b/src/backend/binaryen/glue/transform.ts index f2da321d..2ed49cf9 100644 --- a/src/backend/binaryen/glue/transform.ts +++ b/src/backend/binaryen/glue/transform.ts @@ -242,6 +242,8 @@ export function initStructType( export const i8ArrayType = genarateI8ArrayTypeInfo(); /* array(f64) */ export const numberArrayType = genarateNumberArrayTypeInfo(); +/* array(i32) */ +export const i32ArrayType = genarateI32ArrayTypeInfo(); /* array(stringref) */ export const stringrefArrayType = genarateStringrefArrayTypeInfo(false); /* array(i32) */ @@ -355,6 +357,19 @@ function genarateNumberArrayTypeInfo(): typeInfo { return numberArrayTypeInfo; } +// generate i32 array type +function genarateI32ArrayTypeInfo(): typeInfo { + const i32ArrayTypeInfo = initArrayType( + binaryen.i32, + Packed.Not, + true, + true, + -1, + binaryenCAPI._TypeBuilderCreate(1), + ); + return i32ArrayTypeInfo; +} + // generate string array type function genarateStringArrayTypeInfo(struct_wrap: boolean): typeInfo { const stringTypeInfo = stringType; diff --git a/src/backend/binaryen/lib/init_builtin_api.ts b/src/backend/binaryen/lib/init_builtin_api.ts index 285082d2..aa2612dc 100644 --- a/src/backend/binaryen/lib/init_builtin_api.ts +++ b/src/backend/binaryen/lib/init_builtin_api.ts @@ -38,6 +38,7 @@ import { dataViewTypeInfo, numberArrayStructTypeInfo, numberArrayTypeInfo, + i32ArrayTypeInfo, } from '../glue/packType.js'; import { array_get_data, array_get_length_i32 } from './array_utils.js'; import { SemanticsKind } from '../../../semantics/semantics_nodes.js'; @@ -4982,6 +4983,250 @@ function string_fromCharCode(module: binaryen.Module) { return module.block(null, stmts); } +function WASMStruct_get_field(module: binaryen.Module) { + /* params */ + const typeIdArray_idx = 0; + const idxI32Ref_idx = 1; + const ownerRef_idx = 2; + /* vars */ + const arrLen_i32_idx = 3; + const loopIdx_i32_idx = 4; + const typeIdRef_idx = 5; + const anyTypedRes_Idx = 6; + + const stmts: binaryen.ExpressionRef[] = []; + stmts.push(module.local.set(loopIdx_i32_idx, module.i32.const(0))); + const typeIdArrayValue = module.local.get( + typeIdArray_idx, + i32ArrayTypeInfo.typeRef, + ); + const loopIndexValue = module.local.get(loopIdx_i32_idx, binaryen.i32); + const idxI32RefValue = module.local.get(idxI32Ref_idx, binaryen.i32); + const typeIdRefValue = module.local.get(typeIdRef_idx, binaryen.i32); + const ownerRefValue = module.local.get(ownerRef_idx, binaryen.anyref); + + const arrLen = binaryenCAPI._BinaryenArrayLen(module.ptr, typeIdArrayValue); + stmts.push(module.local.set(arrLen_i32_idx, arrLen)); + /* get the field typeId through for loop */ + const loopLabel = 'for_label'; + const loopCond = module.i32.lt_s( + loopIndexValue, + module.local.get(arrLen_i32_idx, binaryen.i32), + ); + const loopIncrementor = module.local.set( + loopIdx_i32_idx, + module.i32.add(loopIndexValue, module.i32.const(1)), + ); + const loopBody: binaryen.ExpressionRef[] = []; + loopBody.push( + module.if( + module.i32.eq(loopIndexValue, idxI32RefValue), + module.block(null, [ + module.local.set( + typeIdRef_idx, + binaryenCAPI._BinaryenArrayGet( + module.ptr, + typeIdArrayValue, + idxI32RefValue, + i32ArrayTypeInfo.typeRef, + false, + ), + ), + ]), + ), + ); + const flattenLoop: FlattenLoop = { + label: loopLabel, + condition: loopCond, + statements: module.block(null, loopBody), + incrementor: loopIncrementor, + }; + stmts.push( + module.loop( + loopLabel, + FunctionalFuncs.flattenLoopStatement( + module, + flattenLoop, + SemanticsKind.FOR, + ), + ), + ); + const branches: binaryen.ExpressionRef[] = new Array(7); + branches[0] = module.br( + 'case_field_type_is_number', + FunctionalFuncs.isPropTypeIdEqual( + module, + typeIdRefValue, + module.i32.const(PredefinedTypeId.NUMBER), + ), + ); + branches[1] = module.br( + 'case_field_type_is_i32', + FunctionalFuncs.isPropTypeIdEqual( + module, + typeIdRefValue, + module.i32.const(PredefinedTypeId.INT), + ), + ); + branches[2] = module.br( + 'case_field_type_is_i64', + FunctionalFuncs.isPropTypeIdEqual( + module, + typeIdRefValue, + module.i32.const(PredefinedTypeId.WASM_I64), + ), + ); + branches[3] = module.br( + 'case_field_type_is_f32', + FunctionalFuncs.isPropTypeIdEqual( + module, + typeIdRefValue, + module.i32.const(PredefinedTypeId.WASM_F32), + ), + ); + branches[4] = module.br( + 'case_field_type_is_boolean', + FunctionalFuncs.isPropTypeIdEqual( + module, + typeIdRefValue, + module.i32.const(PredefinedTypeId.BOOLEAN), + ), + ); + branches[5] = module.br( + 'case_field_type_is_string', + FunctionalFuncs.isPropTypeIdEqual( + module, + typeIdRefValue, + module.i32.const(PredefinedTypeId.STRING), + ), + ); + branches[6] = module.br('field_type_default'); + + let switchBlock = module.block('case_field_type_is_number', branches); + switchBlock = module.block( + 'case_field_type_is_i32', + [switchBlock].concat( + module.local.set( + anyTypedRes_Idx, + FunctionalFuncs.generateDynNumber( + module, + module.call( + structdyn.StructDyn.struct_get_indirect_f64, + [ownerRefValue, idxI32RefValue], + binaryen.f64, + ), + ), + ), + module.br('field_type_break'), + ), + ); + switchBlock = module.block( + 'case_field_type_is_i64', + [switchBlock].concat( + module.local.set( + anyTypedRes_Idx, + FunctionalFuncs.generateDynNumber( + module, + module.call( + structdyn.StructDyn.struct_get_indirect_i32, + [ownerRefValue, idxI32RefValue], + binaryen.i32, + ), + ), + ), + module.br('field_type_break'), + ), + ); + switchBlock = module.block( + 'case_field_type_is_f32', + [switchBlock].concat( + module.local.set( + anyTypedRes_Idx, + FunctionalFuncs.generateDynNumber( + module, + module.call( + structdyn.StructDyn.struct_get_indirect_i64, + [ownerRefValue, idxI32RefValue], + binaryen.i64, + ), + ), + ), + module.br('field_type_break'), + ), + ); + switchBlock = module.block( + 'case_field_type_is_boolean', + [switchBlock].concat( + module.local.set( + anyTypedRes_Idx, + FunctionalFuncs.generateDynNumber( + module, + module.call( + structdyn.StructDyn.struct_get_indirect_f32, + [ownerRefValue, idxI32RefValue], + binaryen.f32, + ), + ), + ), + module.br('field_type_break'), + ), + ); + switchBlock = module.block( + 'case_field_type_is_string', + [switchBlock].concat( + module.local.set( + anyTypedRes_Idx, + FunctionalFuncs.generateDynBoolean( + module, + module.call( + structdyn.StructDyn.struct_get_indirect_i32, + [ownerRefValue, idxI32RefValue], + binaryen.i32, + ), + ), + ), + module.br('field_type_break'), + ), + ); + switchBlock = module.block( + 'field_type_default', + [switchBlock].concat( + module.local.set( + anyTypedRes_Idx, + FunctionalFuncs.generateDynString( + module, + module.call( + structdyn.StructDyn.struct_get_indirect_anyref, + [ownerRefValue, idxI32RefValue], + binaryen.anyref, + ), + ), + ), + module.br('field_type_break'), + ), + ); + switchBlock = module.block( + 'field_type_break', + [switchBlock].concat( + module.local.set( + anyTypedRes_Idx, + module.call( + structdyn.StructDyn.struct_get_indirect_anyref, + [ownerRefValue, idxI32RefValue], + binaryen.anyref, + ), + ), + module.br('field_type_break'), + ), + ); + stmts.push(switchBlock); + stmts.push( + module.return(module.local.get(anyTypedRes_Idx, binaryen.anyref)), + ); + + return module.block(null, stmts); +} + export function callBuiltInAPIs(module: binaryen.Module) { /** Math.sqrt */ module.addFunction( @@ -6214,6 +6459,17 @@ export function callBuiltInAPIs(module: binaryen.Module) { [binaryen.i32, binaryen.i32], string_fromCharCode(module), ); + module.addFunction( + BuiltinNames.getTupleField, + binaryen.createType([ + i32ArrayTypeInfo.typeRef, + binaryen.i32, + binaryen.anyref, + ]), + binaryen.anyref, + [binaryen.i32, binaryen.i32, binaryen.i32, binaryen.anyref], + WASMStruct_get_field(module), + ); } function addArrayMethod( diff --git a/src/backend/binaryen/utils.ts b/src/backend/binaryen/utils.ts index 422e8c50..b27b1d54 100644 --- a/src/backend/binaryen/utils.ts +++ b/src/backend/binaryen/utils.ts @@ -430,7 +430,7 @@ export namespace FunctionalFuncs { ) { return module.call( dyntype.dyntype_new_number, - [getDynContextRef(module), dynValue], + [getDynContextRef(module), convertTypeToF64(module, dynValue)], dyntype.dyn_value_t, ); } diff --git a/src/backend/binaryen/wasm_expr_gen.ts b/src/backend/binaryen/wasm_expr_gen.ts index ab19a9be..6c6074e3 100644 --- a/src/backend/binaryen/wasm_expr_gen.ts +++ b/src/backend/binaryen/wasm_expr_gen.ts @@ -87,12 +87,14 @@ import { ObjectTypeFlag, Primitive, PrimitiveType, + TupleType, TypeParameterType, UnionType, ValueType, ValueTypeKind, ValueTypeWithArguments, WASMArrayType, + WASMStructType, } from '../../semantics/value_types.js'; import { UnimplementError } from '../../error.js'; import { @@ -110,11 +112,11 @@ import { NewConstructorObjectValue } from '../../semantics/value.js'; import { BuiltinNames } from '../../../lib/builtin/builtin_name.js'; import { dyntype, structdyn } from './lib/dyntype/utils.js'; import { - anyArrayTypeInfo, stringArrayStructTypeInfo, stringrefArrayStructTypeInfo, stringArrayTypeInfo, stringrefArrayTypeInfo, + i32ArrayTypeInfo, } from './glue/packType.js'; import { getBuiltInFuncName } from '../../utils.js'; import { stringTypeInfo } from './glue/packType.js'; @@ -3962,28 +3964,76 @@ export class WASMExpressionGen { case ValueTypeKind.TUPLE: case ValueTypeKind.WASM_STRUCT: { const ownerRef = this.wasmExprGen(owner); - /* TODO: _BinaryenStructSet only accept ts number as index, not binaryen.ExpressionRef as index */ - const idxI32Ref = FunctionalFuncs.convertTypeToI32( - this.module, - this.wasmExprGen(value.index), - ); - let idx = 0; + const ownerHeapTypeRef = + this.wasmTypeGen.getWASMHeapType(ownerType); + const fields = + ownerType instanceof TupleType + ? ownerType.elements + : (ownerType as WASMStructType).tupleType.elements; if (value.index instanceof LiteralValue) { - idx = value.index.value as number; + /* If we can get constant, then invoke BinaryenStructGet */ + const idx = value.index.value as number; + return binaryenCAPI._BinaryenStructGet( + this.module.ptr, + idx, + ownerRef, + ownerHeapTypeRef, + false, + ); } else { - throw new UnimplementError( - `not sure how to convert idxI32Ref to a regular index yet in wasmElemGet`, + if (ownerType instanceof WASMStructType) { + throw new Error( + `index must be a constant in WASMStruct getting`, + ); + } + /* If we can not get constant as index, then invoke struct_get_indirect */ + const idxI32Ref = FunctionalFuncs.convertTypeToI32( + this.module, + this.wasmExprGen(value.index), + ); + + const wasmRawArrayLocal = + this.wasmCompiler.currentFuncCtx!.insertTmpVar( + i32ArrayTypeInfo.typeRef, + ); + const newWasmRawArrayOp = this.module.local.set( + wasmRawArrayLocal.index, + binaryenCAPI._BinaryenArrayNew( + this.module.ptr, + i32ArrayTypeInfo.heapTypeRef, + this.module.i32.const(fields.length), + this.module.i32.const(0), + ), + ); + this.wasmCompiler.currentFuncCtx!.insert(newWasmRawArrayOp); + for (let i = 0; i < fields.length; i++) { + const fieldType = fields[i]; + this.wasmCompiler.currentFuncCtx!.insert( + binaryenCAPI._BinaryenArraySet( + this.module.ptr, + this.module.local.get( + wasmRawArrayLocal.index, + wasmRawArrayLocal.type, + ), + this.module.i32.const(i), + this.module.i32.const(fieldType.typeId), + ), + ); + } + const res = this.module.call( + BuiltinNames.getTupleField, + [ + this.module.local.get( + wasmRawArrayLocal.index, + wasmRawArrayLocal.type, + ), + idxI32Ref, + ownerRef, + ], + binaryen.anyref, ); + return res; } - const ownerHeapTypeRef = - this.wasmTypeGen.getWASMHeapType(ownerType); - return binaryenCAPI._BinaryenStructGet( - this.module.ptr, - idx, - ownerRef, - ownerHeapTypeRef, - false, - ); } default: throw Error(`wasmIdxGet: ${value}`); @@ -4457,8 +4507,8 @@ export class WASMExpressionGen { if ( spreadValue.target.type.kind == ValueTypeKind.ARRAY ) { - const arrayOriHeapType = - this.wasmTypeGen.getWASMArrayOriHeapType( + const arrayOriTypeRef = + this.wasmTypeGen.getWASMArrayOriType( spreadValue.target.type, ); const arrRef = initValueRef; @@ -4469,7 +4519,7 @@ export class WASMExpressionGen { forLoopIdx.index, forLoopIdx.type, ), - arrayOriHeapType, + arrayOriTypeRef, false, ); // box the element by dyntype_new_xxx @@ -4660,6 +4710,10 @@ export class WASMExpressionGen { arrType instanceof ArrayType ? this.wasmTypeGen.getWASMArrayOriHeapType(arrType) : this.wasmTypeGen.getWASMHeapType(arrType); + const arrayOriTypeRef = + arrType instanceof ArrayType + ? this.wasmTypeGen.getWASMArrayOriType(arrType) + : this.wasmTypeGen.getWASMType(arrType); const arrayStructHeapType = this.wasmTypeGen.getWASMHeapType(arrType); const elemType = arrType instanceof ArrayType @@ -4778,7 +4832,7 @@ export class WASMExpressionGen { forLoopIdx.index, forLoopIdx.type, ), - arrayOriHeapType, + arrayOriTypeRef, false, ), );