///|
pub struct InternalAbi {
priv context_argument : @vcode.PhysicalReg
priv result_area_argument : @vcode.PhysicalReg
priv integer_arguments : Array[@vcode.PhysicalReg]
priv fp_arguments : Array[@vcode.PhysicalReg]
priv integer_results : Array[@vcode.PhysicalReg]
priv fp_results : Array[@vcode.PhysicalReg]
}
///|
pub struct InternalCallPlan {
priv arguments : Array[CallArgumentLocation]
priv results : Array[CallResultLocation]
priv stack_size : Int
priv result_area_size : Int
} derive(Eq, Debug)
///|
pub suberror InternalAbiError {
InvalidRegister(role~ : String, index~ : Int)
DuplicateRegister(role~ : String, index~ : Int)
ContextRegisterReused(index~ : Int)
ResultAreaRegisterReused(index~ : Int)
InvalidSignature(message~ : String)
} derive(Eq, Debug)
///|
pub impl Show for InternalAbiError with fn output(self, logger) {
logger.write_string(Repr(self).to_string())
}
///|
fn result_area_register(plan : InternalCallPlan) -> @vcode.PhysicalReg? {
for result in plan.results {
if result is CallResultArea(_, reg) {
return Some(reg)
}
}
None
}
///|
fn checked_registers(
role : String,
indices : Array[Int],
class : @vcode.RegClass,
) -> Array[@vcode.PhysicalReg] raise InternalAbiError {
let registers : Array[@vcode.PhysicalReg] = []
for index in indices {
if index < 0 || index >= 16 {
raise InvalidRegister(role~, index~)
}
if registers.contains(@vcode.PhysicalReg::new(index, class)) {
raise DuplicateRegister(role~, index~)
}
registers.push(@vcode.PhysicalReg::new(index, class))
}
registers
}
///|
pub fn InternalAbi::new(
context_argument : Int,
result_area_argument : Int,
integer_arguments : Array[Int],
fp_arguments : Array[Int],
integer_results : Array[Int],
fp_results : Array[Int],
) -> InternalAbi raise InternalAbiError {
if context_argument < 0 || context_argument >= 16 {
raise InvalidRegister(role="context argument", index=context_argument)
}
let context = @vcode.PhysicalReg::new(context_argument, Int)
if result_area_argument < 0 || result_area_argument >= 16 {
raise InvalidRegister(
role="result area argument",
index=result_area_argument,
)
}
let result_area = @vcode.PhysicalReg::new(result_area_argument, Int)
let integer_arguments = checked_registers(
"integer argument",
integer_arguments,
Int,
)
if integer_arguments.contains(context) {
raise ContextRegisterReused(index=context_argument)
}
if result_area == context || integer_arguments.contains(result_area) {
raise ResultAreaRegisterReused(index=result_area_argument)
}
let integer_results = checked_registers(
"integer result",
integer_results,
Int,
)
if integer_results.contains(result_area) {
raise ResultAreaRegisterReused(index=result_area_argument)
}
{
context_argument: context,
result_area_argument: result_area,
integer_arguments,
fp_arguments: checked_registers("FP argument", fp_arguments, FpVector),
integer_results,
fp_results: checked_registers("FP result", fp_results, FpVector),
}
}
///|
fn InternalAbi::copy(self : InternalAbi) -> InternalAbi {
{
context_argument: self.context_argument,
result_area_argument: self.result_area_argument,
integer_arguments: self.integer_arguments.copy(),
fp_arguments: self.fp_arguments.copy(),
integer_results: self.integer_results.copy(),
fp_results: self.fp_results.copy(),
}
}
///|
fn InternalAbi::call_layout(
self : InternalAbi,
signature : @semantic.Signature,
) -> CallLayout raise InternalAbiError {
if signature.params.is_empty() || signature.params[0] != Ptr64 {
raise InvalidSignature(
message="internal signature must start with an explicit ptr64 execution environment",
)
}
let mut integer_count = 0
for index in 1.. self.integer_arguments.length() {
integer_count - self.integer_arguments.length()
} else {
0
}
let arguments : Array[CallArgumentLocation] = [
CallRegister(self.context_argument),
]
let mut integer_index = 0
let mut fp_index = 0
let mut integer_stack_index = 0
let mut fp_stack_offset = integer_overflow * 8
for index in 1.. (Array[CallResultLocation], Int) {
let locations : Array[CallResultLocation] = []
let mut integer_index = 0
let mut fp_index = 0
let mut integer_area_offset = 0
for ty in result_types {
if @vcode.reg_class_for_value_type(ty) == Int &&
integer_index >= self.integer_results.length() {
integer_area_offset += 8
}
if @vcode.reg_class_for_value_type(ty) == Int {
integer_index += 1
}
}
integer_index = 0
let mut fp_area_offset = integer_area_offset
for ty in result_types {
if @vcode.reg_class_for_value_type(ty) == Int {
if integer_index < self.integer_results.length() {
locations.push(CallResultRegister(self.integer_results[integer_index]))
} else {
locations.push(
CallResultArea(
(integer_index - self.integer_results.length()) * 8,
self.result_area_argument,
),
)
}
integer_index += 1
} else {
if fp_index < self.fp_results.length() {
locations.push(
CallResultRegister(
@vcode.PhysicalReg::new(
self.fp_results[fp_index].id,
@vcode.reg_class_for_value_type(ty),
),
),
)
} else {
let size = call_stack_size(ty)
fp_area_offset = call_align_up(fp_area_offset, size)
locations.push(
CallResultArea(fp_area_offset, self.result_area_argument),
)
fp_area_offset += size
}
fp_index += 1
}
}
(locations, call_align_up(fp_area_offset, 16))
}
///|
fn InternalAbi::call_plan(
self : InternalAbi,
signature : @semantic.Signature,
) -> InternalCallPlan raise InternalAbiError {
let layout = self.call_layout(signature)
let (results, result_area_size) = self.result_layout(signature.results)
{
arguments: layout.arguments,
results,
stack_size: layout.stack_size,
result_area_size,
}
}