Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 29 additions & 2 deletions crates/cranelift/src/func_environ.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -3128,6 +3137,21 @@ impl FuncEnvironment<'_> {
global_index: GlobalIndex,
) -> WasmResult<ir::Value> {
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();
Expand Down Expand Up @@ -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();
Expand Down
4 changes: 3 additions & 1 deletion crates/cranelift/src/translate/environ/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};
34 changes: 33 additions & 1 deletion crates/cranelift/src/translate/environ/spec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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<GlobalConstValue> {
// 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.
Expand Down
2 changes: 1 addition & 1 deletion crates/cranelift/src/translate/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
94 changes: 94 additions & 0 deletions tests/disas/global-get.wat
Original file line number Diff line number Diff line change
@@ -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
;; }