///|
priv struct WasmoonNativeEnvironment {
wasm : @wasm_native.Environment
field_paths : Map[String, Array[Int]]
runtime_symbols : @wasm_milkir.RuntimeSymbols
}
///|
fn wasmoon_native_environment(
use_subtype_indirect_check : Bool,
canonical_type_indices : Array[Int],
) -> WasmoonNativeEnvironment {
let layout = vmcontext_layout()
let field_paths : Map[String, Array[Int]] = Map([])
let runtime_symbols = wasm_runtime_symbols()
let function_table_field = @native.EnvironmentField::new(
"wasmoon.vmctx.function_table",
Ptr64,
)
field_paths.set(function_table_field.name, [layout.func_table_offset])
let canonical_type_indices = canonical_type_indices.copy()
let wasm = @wasm_native.Environment::new(
fn(index) {
let name = "wasm.func.\{index}"
@native.CodeSymbol::new(name)
},
fn(index) {
let field = @native.EnvironmentField::new(
"wasmoon.vmctx.memory.\{index}.base",
Ptr64,
)
let path = if index == 0 {
[layout.memory0_base_offset]
} else {
[layout.memories_offset, index * layout.pointer_stride, 0]
}
field_paths.set(field.name, path)
field
},
function_table_field,
fn(index) {
let field = @native.EnvironmentField::new(
"wasmoon.vmctx.table.\{index}.base",
Ptr64,
)
let path = if index == 0 {
[layout.table0_base_offset]
} else {
[layout.tables_offset, index * layout.pointer_stride]
}
field_paths.set(field.name, path)
field
},
fn(raw_type_index) {
if use_subtype_indirect_check {
return raw_type_index
}
if raw_type_index >= 0 && raw_type_index < canonical_type_indices.length() {
canonical_type_indices[raw_type_index]
} else {
raw_type_index
}
},
fn(helper) {
@native.ExternalSymbol::new(runtime_symbols.symbol_name(helper))
},
)
{ wasm, field_paths, runtime_symbols, }
}
///|
fn wasmoon_allocation_symbols(
environment : WasmoonNativeEnvironment,
) -> Array[@native.ExternalSymbol] {
[
@native.ExternalSymbol::new(
environment.runtime_symbols.symbol_name(GcAllocStructSlow),
),
@native.ExternalSymbol::new(
environment.runtime_symbols.symbol_name(GcAllocArrayFromValuesSlow),
),
@native.ExternalSymbol::new(
environment.runtime_symbols.symbol_name(GcAllocArraySlow),
),
]
}
///|
fn wasmoon_call_abi(
environment : WasmoonNativeEnvironment,
) -> @native_lowering.CallAbiElaboration {
let hidden_safepoints : Array[@native_lowering.HiddenSafepointAbi] = []
for symbol in wasmoon_allocation_symbols(environment) {
hidden_safepoints.push(
@native_lowering.HiddenSafepointAbi::new(symbol, AllocationOperands),
)
}
@native_lowering.CallAbiElaboration::new(
root_scope=@native_lowering.CallerRootScopeAbi::new(
@native.ExternalSymbol::new(gc_push_root_scope_symbol()),
@native.ExternalSymbol::new(gc_pop_root_scope_symbol()),
),
hidden_safepoints~,
)
}
///|
fn wasm_x64_context(
environment : WasmoonNativeEnvironment,
) -> @x64_target.LoweringContext raise JitPipelineError {
let abi = @x64_target.InternalAbi::new(
7,
12,
[6, 2, 1, 8, 9],
[0, 1, 2, 3, 4, 5, 6, 7],
[0, 2, 1, 8, 9, 6, 7, 10],
[0, 1, 2, 3, 4, 5, 6, 7],
) catch {
error => raise X64AbiInvalid(cause=error)
}
@x64_target.LoweringContext::new(abi).with_environment_field_resolver(field => {
environment.field_paths.get(field.name)
})
}
///|
fn wasm_aarch64_context(
environment : WasmoonNativeEnvironment,
) -> @aarch64_target.LoweringContext raise JitPipelineError {
let abi = @aarch64_target.InternalAbi::new(
0,
8,
[1, 2, 3, 4, 5, 6, 7],
[0, 1, 2, 3, 4, 5, 6, 7],
[0, 1, 2, 3, 4, 5, 6, 7],
[0, 1, 2, 3, 4, 5, 6, 7],
) catch {
error => raise AArch64AbiInvalid(cause=error)
}
@aarch64_target.LoweringContext::new(abi).with_environment_field_resolver(field => {
environment.field_paths.get(field.name)
})
}
///|
fn compile_wasm_body_x64_target(
function : @milkir.Function,
validation_context : @wasm_milkir.WasmValidationContext,
use_subtype_indirect_check : Bool,
canonical_type_indices : Array[Int],
record_metrics? : Bool = false,
compilation_session? : @x64_target.CompilationSession = @x64_target.CompilationSession::new(),
) -> X64TargetCompilation raise JitPipelineError {
let environment_tick = detail_metrics_tick(record_metrics)
let environment = wasmoon_native_environment(
use_subtype_indirect_check, canonical_type_indices,
)
let context = wasm_x64_context(environment)
record_compile_subphase("native_environment", environment_tick)
let signature = @milkir_native.function_signature(function, environment_parameters=[
(0, Ptr64),
])
let target_lower_tick : Ref[@perf.PerfTick?] = { val: None, }
let observer = if record_metrics {
Some(target_compile_metrics_observer(target_lower_tick))
} else {
None
}
let session = try {
match observer {
Some(observe) =>
@x64_target.DirectLoweringSession::new(
function.name,
Internal,
signature.params,
signature.results,
context,
on_event=observe,
)
None =>
@x64_target.DirectLoweringSession::new(
function.name,
Internal,
signature.params,
signature.results,
context,
)
}
} catch {
error => raise X64LoweringFailed(cause=error)
}
let native_lower_tick = target_metrics_tick(record_metrics)
@wasm_native.lower_to_sink(
function,
validation_context,
environment.wasm,
session.sink(),
call_abi=wasmoon_call_abi(environment),
verify_input=compiler_validation(),
on_event=if record_metrics && @perf.detail_enabled() {
native_lower_metrics_observer()
} else {
fn(_) { () }
},
) catch {
error => raise NativeLoweringFailed(cause=error)
}
record_target_stage(NativeLowering, native_lower_tick) |> ignore
target_lower_tick.val = target_metrics_tick(record_metrics)
let selected = session.finish_selected(verify_selected=compiler_validation()) catch {
error => raise X64LoweringFailed(cause=error)
}
let compiled = try {
match observer {
Some(observe) =>
compilation_session.compile_selected(
selected,
on_event=observe,
verify_allocation=compiler_validation(),
)
None =>
compilation_session.compile_selected(
selected,
verify_allocation=compiler_validation(),
)
}
} catch {
error => raise X64CompilationFailed(cause=error)
}
let (object, frame_size) = compiled
{ object, frame_size, }
}
///|
fn compile_wasm_body_aarch64_target(
function : @milkir.Function,
validation_context : @wasm_milkir.WasmValidationContext,
use_subtype_indirect_check : Bool,
canonical_type_indices : Array[Int],
record_metrics? : Bool = false,
compilation_session? : @aarch64_target.CompilationSession = @aarch64_target.CompilationSession::new(),
) -> AArch64TargetCompilation raise JitPipelineError {
let environment_tick = detail_metrics_tick(record_metrics)
let environment = wasmoon_native_environment(
use_subtype_indirect_check, canonical_type_indices,
)
let context = wasm_aarch64_context(environment)
record_compile_subphase("native_environment", environment_tick)
let signature = @milkir_native.function_signature(function, environment_parameters=[
(0, Ptr64),
])
let target_lower_tick : Ref[@perf.PerfTick?] = { val: None, }
let observer = if record_metrics {
Some(target_compile_metrics_observer(target_lower_tick))
} else {
None
}
let session = try {
match observer {
Some(observe) =>
@aarch64_target.DirectLoweringSession::new(
function.name,
Internal,
signature.params,
signature.results,
context,
on_event=observe,
)
None =>
@aarch64_target.DirectLoweringSession::new(
function.name,
Internal,
signature.params,
signature.results,
context,
)
}
} catch {
error => raise AArch64LoweringFailed(cause=error)
}
let native_lower_tick = target_metrics_tick(record_metrics)
@wasm_native.lower_to_sink(
function,
validation_context,
environment.wasm,
session.sink(),
call_abi=wasmoon_call_abi(environment),
verify_input=compiler_validation(),
on_event=if record_metrics && @perf.detail_enabled() {
native_lower_metrics_observer()
} else {
fn(_) { () }
},
) catch {
error => raise NativeLoweringFailed(cause=error)
}
record_target_stage(NativeLowering, native_lower_tick) |> ignore
target_lower_tick.val = target_metrics_tick(record_metrics)
let selected = session.finish_selected(verify_selected=compiler_validation()) catch {
error => raise AArch64LoweringFailed(cause=error)
}
let compiled = try {
match observer {
Some(observe) =>
compilation_session.compile_selected(
selected,
on_event=observe,
verify_allocation=compiler_validation(),
)
None =>
compilation_session.compile_selected(
selected,
verify_allocation=compiler_validation(),
)
}
} catch {
error => raise AArch64CompilationFailed(cause=error)
}
let (object, frame_size) = compiled
{ object, frame_size, }
}
///|
pub fn compile_wasm_body_diagnostics_for_target(
function : @milkir.Function,
validation_context : @wasm_milkir.WasmValidationContext,
target : NativeTarget,
use_subtype_indirect_check? : Bool = true,
canonical_type_indices? : Array[Int] = [],
) -> JitPipelineDiagnostics raise JitPipelineError {
let environment = wasmoon_native_environment(
use_subtype_indirect_check, canonical_type_indices,
)
let signature = @milkir_native.function_signature(function, environment_parameters=[
(0, Ptr64),
])
if target is X64 {
let context = wasm_x64_context(environment)
let session = @x64_target.DirectLoweringSession::new(
function.name,
Internal,
signature.params,
signature.results,
context,
) catch {
error => raise X64LoweringFailed(cause=error)
}
@wasm_native.lower_to_sink(
function,
validation_context,
environment.wasm,
session.sink(),
call_abi=wasmoon_call_abi(environment),
) catch {
error => raise NativeLoweringFailed(cause=error)
}
let selected = session.finish_selected() catch {
error => raise X64LoweringFailed(cause=error)
}
let diagnostics = @x64_target.diagnose_selected(selected) catch {
error => raise X64CompilationFailed(cause=error)
}
return {
target_vcode: diagnostics.target_vcode,
allocated_vcode: diagnostics.allocated_vcode,
code_object: code_object_diagnostic_text(diagnostics.object),
machine_code: diagnostics.object
.code()
.map(byte => @types.to_hex_byte(byte.to_int()))
.join(" "),
}
}
let context = wasm_aarch64_context(environment)
let session = @aarch64_target.DirectLoweringSession::new(
function.name,
Internal,
signature.params,
signature.results,
context,
) catch {
error => raise AArch64LoweringFailed(cause=error)
}
@wasm_native.lower_to_sink(
function,
validation_context,
environment.wasm,
session.sink(),
call_abi=wasmoon_call_abi(environment),
) catch {
error => raise NativeLoweringFailed(cause=error)
}
let selected = session.finish_selected() catch {
error => raise AArch64LoweringFailed(cause=error)
}
let diagnostics = @aarch64_target.diagnose_selected(selected) catch {
error => raise AArch64CompilationFailed(cause=error)
}
{
target_vcode: diagnostics.target_vcode,
allocated_vcode: diagnostics.allocated_vcode,
code_object: code_object_diagnostic_text(diagnostics.object),
machine_code: diagnostics.object
.code()
.map(byte => @types.to_hex_byte(byte.to_int()))
.join(" "),
}
}
///|
pub fn plan_wasm_body_aarch64_code_object(
function : @milkir.Function,
validation_context : @wasm_milkir.WasmValidationContext,
use_subtype_indirect_check? : Bool = true,
canonical_type_indices? : Array[Int] = [],
) -> @code_object.UnlinkedCodeObject raise JitPipelineError {
compile_wasm_body_aarch64_target(
function, validation_context, use_subtype_indirect_check, canonical_type_indices,
).object
}
///|
pub fn plan_wasm_body_x64_code_object(
function : @milkir.Function,
validation_context : @wasm_milkir.WasmValidationContext,
use_subtype_indirect_check? : Bool = true,
canonical_type_indices? : Array[Int] = [],
) -> @code_object.UnlinkedCodeObject raise JitPipelineError {
compile_wasm_body_x64_target(
function, validation_context, use_subtype_indirect_check, canonical_type_indices,
).object
}