///|
pub struct Builder[Inst] {
priv function : Function[Inst]
}
///|
pub fn[Inst] Builder::new(
name : String,
parameter_types : Array[ValueType],
) -> Builder[Inst] {
{ function: Function::new(name, Platform, parameter_types, []) }
}
///|
pub fn[Inst] Builder::new_with_results(
name : String,
parameter_types : Array[ValueType],
result_types : Array[ValueType],
) -> Builder[Inst] {
{ function: Function::new(name, Platform, parameter_types, result_types) }
}
///|
pub fn[Inst] Builder::new_with_protocol(
name : String,
protocol : @semantic.CallProtocol,
parameter_types : Array[ValueType],
result_types : Array[ValueType],
) -> Builder[Inst] {
{ function: Function::new(name, protocol, parameter_types, result_types) }
}
///|
pub fn[Inst] Builder::entry_block(self : Builder[Inst]) -> Block {
self.function.entry_block()
}
///|
pub fn[Inst] Builder::parameter(
self : Builder[Inst],
index : Int,
) -> Value raise VCodeBuildError {
match self.function.parameter_at(index) {
Some(value) => value
None => raise InvalidParameter(index~)
}
}
///|
pub fn[Inst] Builder::create_block(
self : Builder[Inst],
parameter_types : Array[ValueType],
) -> Block {
let block = self.function.allocate_block(parameter_types)
self.function.layout.push(block)
block
}
///|
pub fn[Inst] Builder::block_parameter(
self : Builder[Inst],
block : Block,
index : Int,
) -> Value raise VCodeBuildError {
if !self.function.owns_block(block) {
raise ForeignBlock(block~)
}
match self.function.blocks[block.id].parameters.get(index) {
Some(value) => value
None => raise InvalidBlockParameter(block~, index~)
}
}
///|
fn[Inst] Builder::validate_inputs(
self : Builder[Inst],
inputs : Array[Input],
metadata : InstructionMetadata,
) -> Unit raise VCodeBuildError {
for input in inputs {
if !self.function.owns_value(input.value) {
raise ForeignValue(value=input.value)
}
}
for root in metadata.live_gc_roots {
if !self.function.owns_value(root) {
raise ForeignValue(value=root)
}
}
}
///|
fn[Inst] Builder::append_instruction(
self : Builder[Inst],
block : Block,
inst : Inst,
role : InstructionRole,
inputs : Array[Input],
outputs : Array[Output],
successors : Array[Edge],
clobbers : Array[PhysicalReg],
metadata : InstructionMetadata,
) -> (Instruction, Array[Value]) raise VCodeBuildError {
if !self.function.owns_block(block) {
raise ForeignBlock(block~)
}
self.validate_inputs(inputs, metadata)
for input in inputs {
let expected = reg_class_for_value_type(
self.function.value_type(input.value).unwrap(),
)
if input.constraint is Fixed(reg) && reg.class != expected {
raise InvalidOperandConstraint
}
if input.preference is Some(preference) && preference.class != expected {
raise InvalidOperandConstraint
}
}
for output in outputs {
let expected = reg_class_for_value_type(output.ty)
match output.constraint {
Fixed(reg) => if reg.class != expected { raise InvalidOperandConstraint }
TiedTo(tied_to) =>
if tied_to < 0 ||
tied_to >= inputs.length() ||
self.function.value_type(inputs[tied_to].value) != Some(output.ty) {
raise InvalidOperandConstraint
}
AnyLocation | Any => ()
}
if output.preference is Some(preference) && preference.class != expected {
raise InvalidOperandConstraint
}
}
for edge in successors {
if !self.function.owns_block(edge.target) {
raise ForeignBlock(block=edge.target)
}
for argument in edge.arguments {
if !self.function.owns_value(argument) {
raise ForeignValue(value=argument)
}
}
}
let instruction = Instruction::new(
self.function.owner,
self.function.instructions.length(),
)
let results : Array[Value] = []
for output in outputs {
results.push(
self.function.allocate_value(output.ty, InstructionResult(instruction)),
)
}
let operand_start = self.function.operands.length()
for input in inputs {
self.function.operands.push({
value: input.value,
role: Use,
constraint: input.constraint,
preference: input.preference,
timing: input.timing,
tie_id: -1,
})
}
for index, output in outputs {
let operand_index = inputs.length() + index
let tie_id = match output.constraint {
TiedTo(tied_to) => {
self.function.operands[operand_start + tied_to].tie_id = operand_index
operand_index
}
_ => -1
}
self.function.operands.push({
value: results[index],
role: Def,
constraint: output.constraint,
preference: output.preference,
timing: output.timing,
tie_id,
})
}
let clobber_start = self.function.clobbers.length()
self.function.clobbers.push_iter(clobbers.iter())
self.function.instructions.push({
inst,
parent: block,
role,
operand_start,
operand_count: inputs.length() + outputs.length(),
clobber_start,
clobber_count: clobbers.length(),
results,
successors: successors.map(edge => edge.copy()),
metadata: metadata.copy(),
})
(instruction, results)
}
///|
pub fn[Inst] Builder::append_body(
self : Builder[Inst],
block : Block,
inst : Inst,
inputs : Array[Input],
outputs : Array[Output],
clobbers : Array[PhysicalReg],
metadata : InstructionMetadata,
) -> (Instruction, Array[Value]) raise VCodeBuildError {
if !self.function.owns_block(block) {
raise ForeignBlock(block~)
}
if self.function.blocks[block.id].terminator is Some(_) {
raise BlockAlreadyTerminated(block~)
}
let result = self.append_instruction(
block,
inst,
Body,
inputs,
outputs,
[],
clobbers,
metadata,
)
self.function.blocks[block.id].body.push(result.0)
result
}
///|
pub fn[Inst] Builder::set_terminator(
self : Builder[Inst],
block : Block,
inst : Inst,
inputs : Array[Input],
successors : Array[Edge],
clobbers : Array[PhysicalReg],
metadata : InstructionMetadata,
) -> Instruction raise VCodeBuildError {
if !self.function.owns_block(block) {
raise ForeignBlock(block~)
}
if self.function.blocks[block.id].terminator is Some(_) {
raise DuplicateTerminator(block~)
}
let result = self.append_instruction(
block,
inst,
Terminator,
inputs,
[],
successors,
clobbers,
metadata,
)
self.function.blocks[block.id].terminator = Some(result.0)
result.0
}
///|
pub fn[Inst] Builder::finish(self : Builder[Inst]) -> Function[Inst] {
self.function
}