///|
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 : @native.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.blocks[block.id].layout_index = self.function.layout.length()
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::validate_instruction(
self : Builder[Inst],
inputs : Array[Input],
outputs : Array[Output],
successors : Array[Edge],
metadata : InstructionMetadata,
) -> Array[Int]? raise VCodeBuildError {
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.constraint is TiedTo(_) {
raise InvalidOperandConstraint
}
if input.preference is Some(preference) && preference.class != expected {
raise InvalidOperandConstraint
}
}
let mut input_tie_ids : Array[Int]? = None
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() ||
(input_tie_ids is Some(ids) && ids[tied_to] >= 0) ||
self.function.value_type(inputs[tied_to].value) != Some(output.ty) {
raise InvalidOperandConstraint
} else {
if input_tie_ids is None {
input_tie_ids = Some(Array::make(inputs.length(), -1))
}
input_tie_ids.unwrap()[tied_to] = tied_to
}
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)
}
}
}
input_tie_ids
}
///|
fn[Inst] Builder::require_body_open(
self : Builder[Inst],
block : Block,
) -> Unit raise VCodeBuildError {
if !self.function.owns_block(block) {
raise ForeignBlock(block~)
}
if self.function.blocks[block.id].terminator is Some(_) {
raise BlockAlreadyTerminated(block~)
}
}
///|
fn[Inst] Builder::require_terminator_open(
self : Builder[Inst],
block : Block,
) -> Unit raise VCodeBuildError {
if !self.function.owns_block(block) {
raise ForeignBlock(block~)
}
if self.function.blocks[block.id].terminator is Some(_) {
raise DuplicateTerminator(block~)
}
}
///|
fn[Inst] Builder::commit_instruction(
self : Builder[Inst],
block : Block,
inst : Inst,
role : InstructionRole,
inputs : Array[Input],
outputs : Array[Output],
successors : Array[Edge],
clobbers : Array[PhysicalReg],
metadata : InstructionMetadata,
input_tie_ids : Array[Int]?,
) -> (Instruction, Array[Value]) raise VCodeBuildError {
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_index, input in inputs {
let constraint : AllocationConstraint = match input.constraint {
Any => AnyReg
AnyLocation => AnyLocation
Fixed(reg) => FixedReg(reg)
TiedTo(_) => raise InvalidOperandConstraint
}
self.function.operands.push({
vreg: {
id: input.value.id,
class: reg_class_for_value_type(self.function.values[input.value.id].ty),
},
role: Use,
constraint,
preference: input.preference,
timing: input.timing,
tie_id: match input_tie_ids {
Some(ids) => ids[input_index]
None => -1
},
})
}
for index, output in outputs {
let tie_id = match output.constraint {
TiedTo(tied_to) => tied_to
_ => -1
}
let constraint : AllocationConstraint = match output.constraint {
Any | TiedTo(_) => AnyReg
AnyLocation => AnyLocation
Fixed(reg) => FixedReg(reg)
}
self.function.operands.push({
vreg: {
id: results[index].id,
class: reg_class_for_value_type(output.ty),
},
role: Def,
constraint,
preference: output.preference,
timing: output.timing,
tie_id,
})
}
for root in metadata.live_gc_roots {
self.function.operands.push({
vreg: {
id: root.id,
class: reg_class_for_value_type(self.function.values[root.id].ty),
},
role: Use,
constraint: PreservedHome,
preference: None,
timing: Late,
tie_id: -1,
})
}
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(),
allocation_operand_count: inputs.length() +
outputs.length() +
metadata.live_gc_roots.length(),
clobber_start,
clobber_count: clobbers.length(),
results,
successors: successors.map(edge => edge.copy()),
metadata: metadata.copy(),
})
match role {
Body => self.function.blocks[block.id].body.push(instruction)
Terminator => self.function.blocks[block.id].terminator = Some(instruction)
}
(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 {
self.require_body_open(block)
let input_tie_ids = self.validate_instruction(inputs, outputs, [], metadata)
self.commit_instruction(
block,
inst,
Body,
inputs,
outputs,
[],
clobbers,
metadata,
input_tie_ids,
)
}
///|
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 {
self.require_terminator_open(block)
let input_tie_ids = self.validate_instruction(
inputs,
[],
successors,
metadata,
)
self.commit_instruction(
block,
inst,
Terminator,
inputs,
[],
successors,
clobbers,
metadata,
input_tie_ids,
).0
}
///|
pub fn[Inst] Builder::finish(self : Builder[Inst]) -> Function[Inst] {
self.function
}