///|
fn format_value(value : Value) -> String {
"v\{value.id}"
}
///|
fn format_value_with_type(function : Function, value : Value) -> String {
"\{format_value(value)}:\{function.values[value.id].ty}"
}
///|
fn format_values(values : Array[Value]) -> String {
values.map(format_value).join(", ")
}
///|
fn format_signature(signature : Signature) -> String {
let params = signature.params.map(type_ => type_.to_string()).join(", ")
let results = signature.results.map(type_ => type_.to_string()).join(", ")
"(\{params}) -> (\{results})"
}
///|
fn format_protocol(protocol : CallProtocol) -> String {
match protocol {
Internal => "internal"
Platform => "platform"
}
}
///|
fn format_callee(callee : Callee) -> String {
match callee {
Internal(symbol) => "internal @\{symbol.name}"
External(symbol) => "external @\{symbol.name}"
Indirect => "indirect"
}
}
///|
fn format_call(call : SemanticCall) -> String {
"\{format_callee(call.callee)} \{format_signature(call.signature)} [\{format_protocol(call.protocol)}]"
}
///|
fn format_operation(operation : Operation) -> String {
match operation {
I32Const(bits) => "i32.const \{bits}"
I64Const(bits) => "i64.const \{bits}"
F32Const(bits) => "f32.const_bits \{bits}"
F64Const(bits) => "f64.const_bits \{bits}"
V128Const(low, high) => "v128.const_bits \{low}, \{high}"
NullPtr => "ptr.null"
NullGcRef => "gcref.null"
CodeAddress(symbol) => "code.address @\{symbol.name}"
ExternalAddress(symbol) => "external.address @\{symbol.name}"
DataAddress(symbol) => "data.address @\{symbol.name}"
EnvironmentField(field, Stable) => "environment.field.stable @\{field.name}"
EnvironmentField(field, Mutable) => "environment.field @\{field.name}"
StackAddress(object) => "stack.address stack\{object.id}"
Copy => "copy"
Select => "select"
GcRefAddress => "gcref.address"
GcRefFromBits => "gcref.from_bits"
PointerOffset => "pointer.offset"
ReferenceCompare(op) => "reference.compare.\{Repr(op)}"
IntUnary(op) => "int.unary.\{Repr(op)}"
IntBinary(op) => "int.binary.\{Repr(op)}"
IntHighMultiply(signedness) => "int.high_multiply.\{Repr(signedness)}"
IntWithOverflow(op) => "int.with_overflow.\{Repr(op)}"
IntCompare(op) => "int.compare.\{Repr(op)}"
FloatUnary(op) => "float.unary.\{Repr(op)}"
FloatBinary(op) => "float.binary.\{Repr(op)}"
FloatTernary(op) => "float.ternary.\{Repr(op)}"
FloatCompare(op) => "float.compare.\{Repr(op)}"
Convert(op) => "convert.\{Repr(op)}"
Load(spec) => "load \{Repr(spec)}"
Store(spec) => "store \{Repr(spec)}"
AtomicLoad(spec) => "atomic.load \{Repr(spec)}"
AtomicStore(spec) => "atomic.store \{Repr(spec)}"
AtomicRmw(spec, op) => "atomic.rmw.\{Repr(op)} \{Repr(spec)}"
AtomicCompareExchange(spec) => "atomic.compare_exchange \{Repr(spec)}"
AtomicFence => "atomic.fence"
Vector(op) => "vector.\{Repr(op)}"
VectorLoad(spec) => "vector.load \{Repr(spec)}"
VectorStoreLane(spec) => "vector.store_lane \{Repr(spec)}"
Call(call) => "call \{format_call(call)}"
Safepoint(kind) => "safepoint.\{Repr(kind)}"
}
}
///|
fn format_memory_effect(effect : MemoryEffect) -> String {
match effect {
None => "none"
Read => "read"
Write => "write"
ReadWrite => "read-write"
}
}
///|
fn format_semantics(semantics : OperationSemantics) -> String {
let facts : Array[String] = []
if semantics.memory != None {
facts.push("memory=\{format_memory_effect(semantics.memory)}")
}
if semantics.may_trap {
facts.push("trap")
}
if semantics.may_unwind {
facts.push("unwind")
}
if semantics.gc_safepoint {
facts.push("gc-safepoint")
}
if semantics.cancellation_safepoint {
facts.push("cancel-safepoint")
}
if semantics.returns_twice {
facts.push("returns-twice")
}
if facts.is_empty() {
""
} else {
let summary = facts.join(", ")
" [\{summary}]"
}
}
///|
fn format_source(source : SourceLocation?) -> String {
match source {
None => ""
Some(location) =>
" source=\{location.file}:\{location.line}:\{location.column}"
}
}
///|
fn format_roots(roots : Array[Value]) -> String {
if roots.is_empty() {
""
} else {
" roots=[\{format_values(roots)}]"
}
}
///|
fn format_edge(edge : Edge) -> String {
let arguments = format_values(edge.arguments)
if arguments.is_empty() {
"block\{edge.target.id}"
} else {
"block\{edge.target.id}(\{arguments})"
}
}
///|
fn format_terminator(record : TerminatorRecord) -> String {
let text = match record.kind {
Jump(edge) => "jump \{format_edge(edge)}"
Branch(condition, true_edge, false_edge) =>
"branch \{format_value(condition)}, \{format_edge(true_edge)}, \{format_edge(false_edge)}"
Switch(index, cases, default_edge) => {
let arms = cases.map(case => "\{case.bits}: \{format_edge(case.edge)}")
arms.push("default: \{format_edge(default_edge)}")
let formatted_arms = arms.join(", ")
"switch \{format_value(index)} [\{formatted_arms}]"
}
Return(values) => {
let values = format_values(values)
if values.is_empty() {
"return"
} else {
"return \{values}"
}
}
TailCall(call, operands) => {
let operands = format_values(operands)
"tail_call \{format_call(call)} (\{operands})\{format_semantics(call.behavior.semantics())}"
}
NoReturnCall(call, operands) => {
let operands = format_values(operands)
"noreturn_call \{format_call(call)} (\{operands})\{format_semantics(call.behavior.semantics())}"
}
Trap(reason) => "trap \{Repr(reason)}"
}
"\{text}\{format_source(record.metadata.source)}\{format_roots(record.metadata.live_gc_roots)}"
}
///|
/// Render the complete semantic function in a stable target-neutral form.
pub fn Function::print(self : Function) -> String {
let params = self.parameters
.map(value => format_value_with_type(self, value))
.join(", ")
let results = self.signature.results
.map(type_ => type_.to_string())
.join(", ")
let lines : Array[String] = [
"machv \{self.name} [\{format_protocol(self.protocol)}](\{params}) -> (\{results}) {",
]
for object_id, object in self.stack_objects {
lines.push(
"stack\{object_id}: size=\{object.size}, alignment=\{object.alignment}",
)
}
for block_id, block in self.blocks {
let block_params = block.parameters
.map(value => format_value_with_type(self, value))
.join(", ")
if block_params.is_empty() {
lines.push("block\{block_id}:")
} else {
lines.push("block\{block_id}(\{block_params}):")
}
for instruction in block.instructions {
let data = self.instructions[instruction.id]
let results = data.results
.map(value => format_value_with_type(self, value))
.join(", ")
let operands = format_values(data.operands)
let assignment = if results.is_empty() { "" } else { "\{results} = " }
let operand_suffix = if operands.is_empty() { "" } else { " \{operands}" }
lines.push(
" \{assignment}\{format_operation(data.operation)}\{operand_suffix}\{format_semantics(data.operation.semantics())}\{format_source(data.metadata.source)}\{format_roots(data.metadata.live_gc_roots)}\{format_stack_map(data.metadata.stack_map)}",
)
}
match block.terminator {
Some(record) => lines.push(" \{format_terminator(record)}")
None => lines.push(" ")
}
}
lines.push("}")
lines.join("\n")
}
///|
fn format_stack_map(stack_map : StackMapMetadata?) -> String {
match stack_map {
Some(metadata) =>
" stack-map=\{metadata.id}:\{metadata.argument_root_count}"
None => ""
}
}
///|
pub impl Debug for Function with fn to_repr(self) {
Repr::literal(self.print())
}
///|
pub impl Show for Function with fn output(self, logger) {
logger.write_string(self.print())
}