Globals in WebAssembly are different, and I haven't gotten to them yet. Like stack objects, you can't take their address. I think they'll end up looking a lot like stack objects in Cretonne:
- Globals accessed by the function are declared in the preamble, including a name à la
FunctionName
. A byte size is optional, and there is a constant flag and probably an alignment.
- Instructions
global_load
and global_store
work like the stack slot equivalents, and their offsets are verified for sized globals. For unsized globals, these instructions are not sandbox-safe.
- A
global_addr
instruction is used when lowering from WASM, like stack_addr
.
Regarding data initializers, I think your idea makes sense. Here's an LLVM example using somewhat contrived C++ code, but a constant array of enum variants is not unreasonable in Rust:
extern int earray[];
union U {
int *p;
struct S { float c; int *p; } b;
};
U myarray[2] = {
{ .p = &earray[5] },
{ .b = { 3.14, &earray[17] } },
};
To initialize this array, Clang actually has to declare it as a struct type (my formatting):
%union.U = type { %struct.U::S }
%struct.U::S = type { float, i32* }
@earray = external global [0 x i32], align 4
@myarray = global <{ { i32*, [8 x i8] }, %union.U }> <{
{ i32*, [8 x i8] } {
i32* bitcast (i8* getelementptr (i8, i8* bitcast ([0 x i32]* @earray to i8*), i64 20) to i32*),
[8 x i8] undef
},
%union.U {
%struct.U::S {
float 0x40091EB860000000,
i32* bitcast (i8* getelementptr (i8, i8* bitcast ([0 x i32]* @earray to i8*), i64 68) to i32*)
}
}
}>, align 16
The resulting assembly is much nicer:
.section __DATA,__data
.globl _myarray ## @myarray
.align 4
_myarray:
.quad _earray+20
.space 8
.long 1078523331 ## float 3.1400001
.space 4
.quad _earray+68
The LLVM initializer expression does have the advantage that it can specify undef
bits, which LLVM's optimizers will make use of. However, Cretonne won't, so a bag of bytes with relocations makes sense to me.
Sometimes optimizers want to constant fold loads from constant data, and that's where LLVM's representation makes sense. If the declared type matches the loaded type, the load can simply be replaced with the constant expression from the initializer. It is important that this kind of constant folding works when relocations are present. Constant folding vtable loads can enable more inlining.