| // Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| import 'package:kernel/ast.dart'; |
| |
| import 'package:wasm_builder/wasm_builder.dart' as w; |
| |
| import 'translator.dart'; |
| |
| /// Handles lazy initialization of static fields. |
| class Globals { |
| final Translator translator; |
| |
| final Map<Field, w.Global> _globals = {}; |
| final Map<Field, w.BaseFunction> _globalInitializers = {}; |
| final Map<Field, w.Global> _globalInitializedFlag = {}; |
| final Map<w.FunctionType, w.BaseFunction> _dummyFunctions = {}; |
| final Map<w.HeapType, w.Global> _dummyValues = {}; |
| late final w.Global dummyStructGlobal; |
| |
| w.ModuleBuilder get m => translator.m; |
| |
| Globals(this.translator) { |
| _initDummyValues(); |
| } |
| |
| void _initDummyValues() { |
| // Create dummy struct for anyref/eqref/structref dummy values |
| w.StructType structType = m.types.defineStruct("#DummyStruct"); |
| final dummyStructGlobalInit = m.globals.define( |
| w.GlobalType(w.RefType.struct(nullable: false), mutable: false)); |
| final ib = dummyStructGlobalInit.initializer; |
| ib.struct_new(structType); |
| ib.end(); |
| _dummyValues[w.HeapType.any] = dummyStructGlobalInit; |
| _dummyValues[w.HeapType.eq] = dummyStructGlobalInit; |
| _dummyValues[w.HeapType.struct] = dummyStructGlobalInit; |
| dummyStructGlobal = dummyStructGlobalInit; |
| } |
| |
| /// Provide a dummy function with the given signature. Used for empty entries |
| /// in vtables and for dummy values of function reference type. |
| w.BaseFunction getDummyFunction(w.FunctionType type) { |
| return _dummyFunctions.putIfAbsent(type, () { |
| final function = m.functions.define(type, "#dummy function $type"); |
| final b = function.body; |
| b.unreachable(); |
| b.end(); |
| return function; |
| }); |
| } |
| |
| /// Returns whether the given function was provided by [getDummyFunction]. |
| bool isDummyFunction(w.BaseFunction function) { |
| return _dummyFunctions[function.type] == function; |
| } |
| |
| w.Global? _prepareDummyValue(w.ValueType type) { |
| if (type is w.RefType && !type.nullable) { |
| w.HeapType heapType = type.heapType; |
| w.Global? foundGlobal = _dummyValues[heapType]; |
| if (foundGlobal != null) return foundGlobal; |
| w.GlobalBuilder? global; |
| if (heapType is w.DefType) { |
| if (heapType is w.StructType) { |
| for (w.FieldType field in heapType.fields) { |
| _prepareDummyValue(field.type.unpacked); |
| } |
| global = m.globals.define(w.GlobalType(type, mutable: false)); |
| final ib = global.initializer; |
| for (w.FieldType field in heapType.fields) { |
| instantiateDummyValue(ib, field.type.unpacked); |
| } |
| ib.struct_new(heapType); |
| ib.end(); |
| } else if (heapType is w.ArrayType) { |
| global = m.globals.define(w.GlobalType(type, mutable: false)); |
| final ib = global.initializer; |
| ib.array_new_fixed(heapType, 0); |
| ib.end(); |
| } else if (heapType is w.FunctionType) { |
| global = m.globals.define(w.GlobalType(type, mutable: false)); |
| final ib = global.initializer; |
| ib.ref_func(getDummyFunction(heapType)); |
| ib.end(); |
| } |
| _dummyValues[heapType] = global!; |
| } |
| return global; |
| } |
| |
| return null; |
| } |
| |
| /// Produce a dummy value of any Wasm type. For non-nullable reference types, |
| /// the value is constructed in a global initializer, and the instantiation |
| /// of the value merely reads the global. |
| void instantiateDummyValue(w.InstructionsBuilder b, w.ValueType type) { |
| switch (type) { |
| case w.NumType.i32: |
| b.i32_const(0); |
| break; |
| case w.NumType.i64: |
| b.i64_const(0); |
| break; |
| case w.NumType.f32: |
| b.f32_const(0); |
| break; |
| case w.NumType.f64: |
| b.f64_const(0); |
| break; |
| default: |
| if (type is w.RefType) { |
| w.HeapType heapType = type.heapType; |
| if (type.nullable) { |
| b.ref_null(heapType.bottomType); |
| } else { |
| b.global_get(_prepareDummyValue(type)!); |
| } |
| } else { |
| throw "Unsupported global type $type ($type)"; |
| } |
| break; |
| } |
| } |
| |
| Constant? _getConstantInitializer(Field variable) { |
| Expression? init = variable.initializer; |
| if (init == null || init is NullLiteral) return NullConstant(); |
| if (init is IntLiteral) return IntConstant(init.value); |
| if (init is DoubleLiteral) return DoubleConstant(init.value); |
| if (init is BoolLiteral) return BoolConstant(init.value); |
| if (init is StringLiteral) return StringConstant(init.value); |
| if (init is ConstantExpression) return init.constant; |
| return null; |
| } |
| |
| /// Return (and if needed create) the Wasm global corresponding to a static |
| /// field. |
| w.Global getGlobal(Field field) { |
| assert(!field.isLate); |
| return _globals.putIfAbsent(field, () { |
| final Constant? init = _getConstantInitializer(field); |
| w.ValueType type = translator.translateTypeOfField(field); |
| if (init != null && |
| !(translator.constants.ensureConstant(init)?.isLazy ?? false)) { |
| // Initialized to a constant |
| final global = |
| m.globals.define(w.GlobalType(type, mutable: !field.isFinal)); |
| translator.constants |
| .instantiateConstant(global.initializer, init, type); |
| global.initializer.end(); |
| return global; |
| } else { |
| if (type is w.RefType && !type.nullable) { |
| // Null signals uninitialized |
| type = type.withNullability(true); |
| } else { |
| // Explicit initialization flag |
| final flag = m.globals.define(w.GlobalType(w.NumType.i32)); |
| flag.initializer.i32_const(0); |
| flag.initializer.end(); |
| _globalInitializedFlag[field] = flag; |
| } |
| |
| final global = m.globals.define(w.GlobalType(type)); |
| instantiateDummyValue(global.initializer, type); |
| global.initializer.end(); |
| |
| _globalInitializers[field] = |
| translator.functions.getFunction(field.fieldReference); |
| return global; |
| } |
| }); |
| } |
| |
| /// Return the Wasm global containing the flag indicating whether this static |
| /// field has been initialized, if such a flag global is needed. |
| /// |
| /// Note that [getGlobal] must have been called for the field beforehand. |
| w.Global? getGlobalInitializedFlag(Field variable) { |
| return _globalInitializedFlag[variable]; |
| } |
| |
| /// Emit code to read a static field. |
| w.ValueType readGlobal(w.InstructionsBuilder b, Field variable) { |
| w.Global global = getGlobal(variable); |
| w.BaseFunction? initFunction = _globalInitializers[variable]; |
| if (initFunction == null) { |
| // Statically initialized |
| b.global_get(global); |
| return global.type.type; |
| } |
| w.Global? flag = _globalInitializedFlag[variable]; |
| if (flag != null) { |
| // Explicit initialization flag |
| assert(global.type.type == initFunction.type.outputs.single); |
| b.global_get(flag); |
| b.if_(const [], [global.type.type]); |
| b.global_get(global); |
| b.else_(); |
| b.call(initFunction); |
| b.end(); |
| } else { |
| // Null signals uninitialized |
| w.Label block = b.block(const [], [initFunction.type.outputs.single]); |
| b.global_get(global); |
| b.br_on_non_null(block); |
| b.call(initFunction); |
| b.end(); |
| } |
| return initFunction.type.outputs.single; |
| } |
| } |