diff --git a/crates/cranelift/src/func_environ.rs b/crates/cranelift/src/func_environ.rs index 6ae2a452fe7d..eb9581f9e3de 100644 --- a/crates/cranelift/src/func_environ.rs +++ b/crates/cranelift/src/func_environ.rs @@ -3,8 +3,8 @@ pub(crate) mod stack_switching; use crate::compiler::Compiler; use crate::translate::{ - FuncTranslationStacks, GlobalVariable, Heap, HeapData, StructFieldsVec, TableData, TableSize, - TargetEnvironment, + FuncTranslationStacks, GlobalConstValue, GlobalVariable, Heap, HeapData, StructFieldsVec, + TableData, TableSize, TargetEnvironment, }; use crate::{BuiltinFunctionSignatures, TRAP_INTERNAL_ASSERT}; use cranelift_codegen::cursor::FuncCursor; @@ -1426,6 +1426,15 @@ impl FuncEnvironment<'_> { return GlobalVariable::Custom; } + if !self.module.globals[index].mutability { + if let Some(index) = self.module.defined_global_index(index) { + let init = &self.module.global_initializers[index]; + if let Some(value) = GlobalConstValue::const_eval(init) { + return GlobalVariable::Constant { value }; + } + } + } + let (gv, offset) = self.get_global_location(func, index); GlobalVariable::Memory { gv, @@ -3128,6 +3137,21 @@ impl FuncEnvironment<'_> { global_index: GlobalIndex, ) -> WasmResult { match self.get_or_create_global(builder.func, global_index) { + GlobalVariable::Constant { value } => match value { + GlobalConstValue::I32(x) => Ok(builder.ins().iconst(ir::types::I32, i64::from(x))), + GlobalConstValue::I64(x) => Ok(builder.ins().iconst(ir::types::I64, x)), + GlobalConstValue::F32(x) => { + Ok(builder.ins().f32const(ir::immediates::Ieee32::with_bits(x))) + } + GlobalConstValue::F64(x) => { + Ok(builder.ins().f64const(ir::immediates::Ieee64::with_bits(x))) + } + GlobalConstValue::V128(x) => { + let data = x.to_le_bytes().to_vec().into(); + let handle = builder.func.dfg.constants.insert(data); + Ok(builder.ins().vconst(ir::types::I8X16, handle)) + } + }, GlobalVariable::Memory { gv, offset, ty } => { let addr = builder.ins().global_value(self.pointer_type(), gv); let mut flags = ir::MemFlags::trusted(); @@ -3178,6 +3202,9 @@ impl FuncEnvironment<'_> { val: ir::Value, ) -> WasmResult<()> { match self.get_or_create_global(builder.func, global_index) { + GlobalVariable::Constant { .. } => { + unreachable!("validation checks that Wasm cannot `global.set` constant globals") + } GlobalVariable::Memory { gv, offset, ty } => { let addr = builder.ins().global_value(self.pointer_type(), gv); let mut flags = ir::MemFlags::trusted(); diff --git a/crates/cranelift/src/translate/environ/mod.rs b/crates/cranelift/src/translate/environ/mod.rs index d4bc1db7e748..6de27513dc72 100644 --- a/crates/cranelift/src/translate/environ/mod.rs +++ b/crates/cranelift/src/translate/environ/mod.rs @@ -3,4 +3,6 @@ #[macro_use] mod spec; -pub use crate::translate::environ::spec::{GlobalVariable, StructFieldsVec, TargetEnvironment}; +pub use crate::translate::environ::spec::{ + GlobalConstValue, GlobalVariable, StructFieldsVec, TargetEnvironment, +}; diff --git a/crates/cranelift/src/translate/environ/spec.rs b/crates/cranelift/src/translate/environ/spec.rs index 54e47f634b5f..766a18e52f51 100644 --- a/crates/cranelift/src/translate/environ/spec.rs +++ b/crates/cranelift/src/translate/environ/spec.rs @@ -10,11 +10,17 @@ use cranelift_codegen::ir; use cranelift_codegen::ir::immediates::Offset32; use cranelift_codegen::isa::TargetFrontendConfig; use smallvec::SmallVec; -use wasmtime_environ::{Tunables, TypeConvert, WasmHeapType}; +use wasmtime_environ::{ConstExpr, ConstOp, Tunables, TypeConvert, WasmHeapType}; /// The value of a WebAssembly global variable. #[derive(Clone, Copy)] pub enum GlobalVariable { + /// The global is known to be a constant value. + Constant { + /// The global's known value. + value: GlobalConstValue, + }, + /// This is a variable in memory that should be referenced through a `GlobalValue`. Memory { /// The address of the global variable storage. @@ -29,6 +35,32 @@ pub enum GlobalVariable { Custom, } +/// A global's constant value, known at compile time. +#[derive(Clone, Copy)] +pub enum GlobalConstValue { + I32(i32), + I64(i64), + F32(u32), + F64(u64), + V128(u128), +} + +impl GlobalConstValue { + /// Attempt to evaluate the given const-expr at compile time. + pub fn const_eval(init: &ConstExpr) -> Option { + // TODO: Actually maintain an evaluation stack and handle `i32.add`, + // `i32.sub`, etc... const ops. + match init.ops() { + [ConstOp::I32Const(x)] => Some(Self::I32(*x)), + [ConstOp::I64Const(x)] => Some(Self::I64(*x)), + [ConstOp::F32Const(x)] => Some(Self::F32(*x)), + [ConstOp::F64Const(x)] => Some(Self::F64(*x)), + [ConstOp::V128Const(x)] => Some(Self::V128(*x)), + _ => None, + } + } +} + /// Environment affecting the translation of a WebAssembly. pub trait TargetEnvironment: TypeConvert { /// Get the information needed to produce Cranelift IR for the given target. diff --git a/crates/cranelift/src/translate/mod.rs b/crates/cranelift/src/translate/mod.rs index 7effeb759be3..6ca0b40760bb 100644 --- a/crates/cranelift/src/translate/mod.rs +++ b/crates/cranelift/src/translate/mod.rs @@ -18,7 +18,7 @@ mod stack; mod table; mod translation_utils; -pub use self::environ::{GlobalVariable, StructFieldsVec, TargetEnvironment}; +pub use self::environ::{GlobalConstValue, GlobalVariable, StructFieldsVec, TargetEnvironment}; pub use self::func_translator::FuncTranslator; pub use self::heap::{Heap, HeapData}; pub use self::stack::FuncTranslationStacks; diff --git a/tests/disas/global-get.wat b/tests/disas/global-get.wat new file mode 100644 index 000000000000..ca3eeaa4d9c2 --- /dev/null +++ b/tests/disas/global-get.wat @@ -0,0 +1,94 @@ +;;! target = "x86_64" + +(module + ;; Imported and immutable: should become a vmctx load. + (import "env" "a" (global $imp-imm i32)) + + ;; Imported and mutable: should become a vmctx load. + (import "env" "b" (global $imp-mut (mut i32))) + + ;; Defined and immutable: should become a constant. + (global $def-imm i32 (i32.const 42)) + + ;; Defined and mutable: should become a vmctx load. + (global $def-mut (mut i32) (i32.const 36)) + + (func $f0 (result i32) + (global.get $imp-imm) + ) + + (func $f1 (result i32) + (global.get $imp-mut) + ) + + (func $f2 (result i32) + (global.get $def-imm) + ) + + (func $f3 (result i32) + (global.get $def-mut) + ) +) + +;; function u0:0(i64 vmctx, i64) -> i32 tail { +;; gv0 = vmctx +;; gv1 = load.i64 notrap aligned readonly gv0+8 +;; gv2 = load.i64 notrap aligned gv1+16 +;; gv3 = vmctx +;; gv4 = load.i64 notrap aligned readonly can_move gv3+48 +;; stack_limit = gv2 +;; +;; block0(v0: i64, v1: i64): +;; @003d v3 = load.i64 notrap aligned readonly can_move v0+48 +;; @003d v4 = load.i32 notrap aligned table v3 +;; @003f jump block1 +;; +;; block1: +;; @003f return v4 +;; } +;; +;; function u0:1(i64 vmctx, i64) -> i32 tail { +;; gv0 = vmctx +;; gv1 = load.i64 notrap aligned readonly gv0+8 +;; gv2 = load.i64 notrap aligned gv1+16 +;; gv3 = vmctx +;; gv4 = load.i64 notrap aligned readonly can_move gv3+72 +;; stack_limit = gv2 +;; +;; block0(v0: i64, v1: i64): +;; @0042 v3 = load.i64 notrap aligned readonly can_move v0+72 +;; @0042 v4 = load.i32 notrap aligned table v3 +;; @0044 jump block1 +;; +;; block1: +;; @0044 return v4 +;; } +;; +;; function u0:2(i64 vmctx, i64) -> i32 tail { +;; gv0 = vmctx +;; gv1 = load.i64 notrap aligned readonly gv0+8 +;; gv2 = load.i64 notrap aligned gv1+16 +;; stack_limit = gv2 +;; +;; block0(v0: i64, v1: i64): +;; @0047 v3 = iconst.i32 42 +;; @0049 jump block1 +;; +;; block1: +;; @0049 return v3 ; v3 = 42 +;; } +;; +;; function u0:3(i64 vmctx, i64) -> i32 tail { +;; gv0 = vmctx +;; gv1 = load.i64 notrap aligned readonly gv0+8 +;; gv2 = load.i64 notrap aligned gv1+16 +;; gv3 = vmctx +;; stack_limit = gv2 +;; +;; block0(v0: i64, v1: i64): +;; @004c v4 = load.i32 notrap aligned table v0+112 +;; @004e jump block1 +;; +;; block1: +;; @004e return v4 +;; }