///|
/// Safe semantic-construction seam used by MilkIR dialect adapters.
///
/// The context exposes only verified operands, explicit environment values,
/// target-neutral MachV construction, and structured control-flow builders. It
/// does not expose mutable MachV storage, target instructions, or ABI details.
struct InstructionContext {
builder : @machv.FunctionBuilder
operands : Array[@machv.Value]
operand_types : Array[@machv.ValueType]
result_types : Array[@machv.ValueType]
function_result_types : Array[@machv.ValueType]
environment : Array[@machv.Value]
source : @machv.SourceLocation?
live_gc_roots : Array[@machv.Value]
completed : Ref[Bool]
terminates_block : Ref[Bool]
error : Ref[String?]
complete : (Array[@machv.Value]) -> Unit
}
///|
pub fn InstructionContext::new(
builder : @machv.FunctionBuilder,
operands : Array[@machv.Value],
operand_types : Array[@machv.ValueType],
result_types : Array[@machv.ValueType],
function_result_types : Array[@machv.ValueType],
environment : Array[@machv.Value],
source : @machv.SourceLocation?,
live_gc_roots : Array[@machv.Value],
complete : (Array[@machv.Value]) -> Unit,
) -> InstructionContext {
{
builder,
operands: operands.copy(),
operand_types: operand_types.copy(),
result_types: result_types.copy(),
function_result_types: function_result_types.copy(),
environment: environment.copy(),
source,
live_gc_roots: live_gc_roots.copy(),
completed: Ref(false),
terminates_block: Ref(false),
error: Ref(None),
complete,
}
}
///|
fn InstructionContext::record_error(
self : InstructionContext,
message : String,
) -> Unit {
if self.error.val is None {
self.error.val = Some(message)
}
}
///|
fn InstructionContext::complete_once(
self : InstructionContext,
results : Array[@machv.Value],
) -> Unit {
if self.completed.val {
self.record_error("dialect instruction completed more than once")
return
}
if results.length() != self.result_types.length() {
self.record_error(
"dialect instruction produced \{results.length()} results, expected \{self.result_types.length()}",
)
return
}
self.completed.val = true
(self.complete)(results)
}
///|
fn InstructionContext::instruction_metadata(
self : InstructionContext,
include_gc_roots : Bool,
) -> @machv.InstructionMetadata {
@machv.InstructionMetadata::new(
self.source,
if include_gc_roots {
self.live_gc_roots
} else {
[]
},
)
}
///|
fn InstructionContext::terminator_metadata(
self : InstructionContext,
) -> @machv.TerminatorMetadata {
@machv.TerminatorMetadata::new(self.source, self.live_gc_roots)
}
///|
pub fn InstructionContext::operands(
self : InstructionContext,
) -> Array[@machv.Value] {
self.operands.copy()
}
///|
pub fn InstructionContext::result_types(
self : InstructionContext,
) -> Array[@machv.ValueType] {
self.result_types.copy()
}
///|
pub fn InstructionContext::operand_types(
self : InstructionContext,
) -> Array[@machv.ValueType] {
self.operand_types.copy()
}
///|
pub fn InstructionContext::function_result_types(
self : InstructionContext,
) -> Array[@machv.ValueType] {
self.function_result_types.copy()
}
///|
pub fn InstructionContext::environment(
self : InstructionContext,
) -> Array[@machv.Value] {
self.environment.copy()
}
///|
pub fn InstructionContext::source(
self : InstructionContext,
) -> @machv.SourceLocation? {
self.source
}
///|
pub fn InstructionContext::emit(
self : InstructionContext,
operation : @machv.Operation,
operands : Array[@machv.Value],
result_types : Array[@machv.ValueType],
) -> Array[@machv.Value] {
self.builder.emit_with_metadata(
operation,
operands,
result_types,
self.instruction_metadata(false),
)
}
///|
pub fn InstructionContext::finish_operation(
self : InstructionContext,
operation : @machv.Operation,
operands : Array[@machv.Value],
) -> Unit {
let results = self.builder.emit_with_metadata(
operation,
operands,
self.result_types,
self.instruction_metadata(false),
)
self.complete_once(results)
}
///|
pub fn InstructionContext::emit_call(
self : InstructionContext,
call : @machv.SemanticCall,
operands : Array[@machv.Value],
result_types : Array[@machv.ValueType],
) -> Array[@machv.Value] {
self.builder.emit_with_metadata(
Call(call),
operands,
result_types,
self.instruction_metadata(true),
)
}
///|
pub fn InstructionContext::emit_call_without_roots(
self : InstructionContext,
call : @machv.SemanticCall,
operands : Array[@machv.Value],
result_types : Array[@machv.ValueType],
) -> Array[@machv.Value] {
self.builder.emit_with_metadata(
Call(call),
operands,
result_types,
self.instruction_metadata(false),
)
}
///|
pub fn InstructionContext::finish_call(
self : InstructionContext,
call : @machv.SemanticCall,
operands : Array[@machv.Value],
) -> Unit {
let results = self.emit_call(call, operands, self.result_types)
self.complete_once(results)
}
///|
pub fn InstructionContext::finish_call_without_roots(
self : InstructionContext,
call : @machv.SemanticCall,
operands : Array[@machv.Value],
) -> Unit {
let results = self.emit_call_without_roots(call, operands, self.result_types)
self.complete_once(results)
}
///|
pub fn InstructionContext::forward_results(
self : InstructionContext,
values : Array[@machv.Value],
) -> Unit {
if values.length() != self.result_types.length() {
self.record_error(
"dialect instruction forwarded \{values.length()} results, expected \{self.result_types.length()}",
)
return
}
let results : Array[@machv.Value] = []
for index, value in values {
let copied = self.builder.emit_with_metadata(
Copy,
[value],
[self.result_types[index]],
self.instruction_metadata(false),
)
if copied.length() != 1 {
self.record_error("failed to construct dialect result copy")
return
}
results.push(copied[0])
}
self.complete_once(results)
}
///|
pub fn InstructionContext::finish_void(self : InstructionContext) -> Unit {
self.complete_once([])
}
///|
pub fn InstructionContext::create_block(
self : InstructionContext,
parameter_types : Array[@machv.ValueType],
) -> @machv.Block {
self.builder.create_block(parameter_types)
}
///|
pub fn InstructionContext::create_stack_object(
self : InstructionContext,
size : Int,
alignment : Int,
) -> @machv.StackObject {
self.builder.create_stack_object(size, alignment)
}
///|
pub fn InstructionContext::block_parameters(
self : InstructionContext,
block : @machv.Block,
) -> Array[@machv.Value] {
self.builder.block_parameters(block)
}
///|
pub fn InstructionContext::switch_to_block(
self : InstructionContext,
block : @machv.Block,
) -> Unit {
self.builder.switch_to_block(block)
}
///|
pub fn InstructionContext::jump(
self : InstructionContext,
target : @machv.Block,
arguments : Array[@machv.Value],
) -> Unit {
self.builder.jump(target, arguments)
}
///|
pub fn InstructionContext::branch(
self : InstructionContext,
condition : @machv.Value,
true_target : @machv.Block,
true_arguments : Array[@machv.Value],
false_target : @machv.Block,
false_arguments : Array[@machv.Value],
) -> Unit {
self.builder.branch(
condition, true_target, true_arguments, false_target, false_arguments,
)
}
///|
pub fn InstructionContext::trap(
self : InstructionContext,
reason : @machv.TrapReason,
) -> Unit {
self.builder.trap(reason)
if self.source is Some(source) {
self.builder.set_terminator_source(source)
}
self.terminates_block.val = true
self.finish_void()
}
///|
/// Terminate an adapter-created side block without completing the source
/// dialect instruction. The adapter must switch to a continuation block and
/// complete the instruction there.
pub fn InstructionContext::set_trap_terminator(
self : InstructionContext,
reason : @machv.TrapReason,
) -> Unit {
self.builder.trap(reason)
if self.source is Some(source) {
self.builder.set_terminator_source(source)
}
}
///|
pub fn InstructionContext::tail_call(
self : InstructionContext,
call : @machv.SemanticCall,
operands : Array[@machv.Value],
) -> Unit {
self.builder.tail_call_with_metadata(
call,
operands,
self.terminator_metadata(),
)
self.terminates_block.val = true
self.finish_void()
}
///|
pub fn InstructionContext::noreturn_call(
self : InstructionContext,
call : @machv.SemanticCall,
operands : Array[@machv.Value],
) -> Unit {
let metadata = if call.behavior.gc_safepoint {
self.terminator_metadata()
} else {
@machv.TerminatorMetadata::new(self.source, [])
}
self.builder.noreturn_call_with_metadata(call, operands, metadata)
self.terminates_block.val = true
self.finish_void()
}
///|
pub fn InstructionContext::terminates_block(self : InstructionContext) -> Bool {
self.terminates_block.val
}
///|
pub fn InstructionContext::status(self : InstructionContext) -> String? {
match self.error.val {
Some(message) => Some(message)
None =>
if self.completed.val {
None
} else {
Some("dialect instruction did not complete its result contract")
}
}
}