Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions capc/src/codegen/abi_quirks.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
//! ABI quirks and lowering helpers.
//!
//! These helpers are the single source of truth for the special-case ABI
//! shapes we use to lower `Result` values (ResultString and ResultOut).

use crate::abi::AbiType;

use super::FnSig;

/// ResultString ABI uses a u64 length slot across targets.
pub fn result_string_len_bytes() -> u32 {
8
}

/// Return true if the ABI type is lowered as ResultString.
pub fn is_result_string(ty: &AbiType) -> bool {
matches!(ty, AbiType::ResultString)
}

/// Return true if the ABI type is lowered using ResultOut parameters.
pub fn is_result_out(ty: &AbiType) -> bool {
matches!(ty, AbiType::ResultOut(_, _))
}

/// Return true if the ABI type uses any Result lowering.
pub fn is_result_lowering(ty: &AbiType) -> bool {
is_result_string(ty) || is_result_out(ty)
}

/// Return true if a signature mismatch is explained by Result lowering.
pub fn abi_sig_requires_lowering(abi_sig: &FnSig, sig: &FnSig) -> bool {
abi_sig != sig && is_result_lowering(&abi_sig.ret)
}

/// Error message used when a layout is requested for a lowered Result ABI.
pub fn result_lowering_layout_error() -> &'static str {
"layout for result out params"
}

/// Error message used when a Result ABI form is not supported.
pub fn result_abi_mismatch_error() -> &'static str {
"result abi mismatch"
}

/// Error message used when ResultOut lowering is requested but unsupported.
pub fn result_out_params_error() -> &'static str {
"result out params"
}

/// Error message used when ResultString lowering is requested but unsupported.
pub fn result_string_params_error() -> &'static str {
"result abi"
}
88 changes: 57 additions & 31 deletions capc/src/codegen/emit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ use super::{
CodegenError, EnumIndex, Flow, FnInfo, LocalValue, ResultKind, ResultShape, StructLayoutIndex,
TypeLayout, ValueRepr,
};
use super::abi_quirks;
use super::layout::{align_to, resolve_struct_layout, type_layout_for_abi};
use super::sig_to_clif;

Expand Down Expand Up @@ -422,7 +423,9 @@ fn emit_hir_expr_inner(
if let crate::typeck::Ty::Path(ty_name, args) = &variant.enum_ty.ty {
if ty_name == "Result" && args.len() == 2 {
let AbiType::Result(ok_abi, err_abi) = &variant.enum_ty.abi else {
return Err(CodegenError::Unsupported("result abi mismatch".to_string()));
return Err(CodegenError::Unsupported(
abi_quirks::result_abi_mismatch_error().to_string(),
));
};
let ok_ty = crate::hir::HirType {
ty: args[0].clone(),
Expand Down Expand Up @@ -538,7 +541,9 @@ fn emit_hir_expr_inner(
let ret_value = match &try_expr.ret_ty.ty {
crate::typeck::Ty::Path(name, args) if name == "Result" && args.len() == 2 => {
let AbiType::Result(ok_abi, _err_abi) = &try_expr.ret_ty.abi else {
return Err(CodegenError::Unsupported("result abi mismatch".to_string()));
return Err(CodegenError::Unsupported(
abi_quirks::result_abi_mismatch_error().to_string(),
));
};
let ok_ty = crate::hir::HirType {
ty: args[0].clone(),
Expand Down Expand Up @@ -621,7 +626,7 @@ fn emit_hir_expr_inner(
let mut result_out = None;
let abi_sig = info.abi_sig.as_ref().unwrap_or(&info.sig);

if abi_sig.ret == AbiType::ResultString {
if abi_quirks::is_result_string(&abi_sig.ret) {
let ptr_ty = module.isa().pointer_type();
let slots = result_string_slots(builder, ptr_ty);
push_result_string_out_params(builder, ptr_ty, &slots, &mut args);
Expand Down Expand Up @@ -680,7 +685,7 @@ fn emit_hir_expr_inner(
let results = builder.inst_results(call_inst).to_vec();

// Handle result unpacking (same logic as AST version)
if abi_sig.ret == AbiType::ResultString {
if abi_quirks::is_result_string(&abi_sig.ret) {
let tag = results
.get(0)
.ok_or_else(|| CodegenError::Codegen("missing result tag".to_string()))?;
Expand All @@ -691,17 +696,21 @@ fn emit_hir_expr_inner(
match &info.sig.ret {
AbiType::Result(ok_ty, err_ty) => {
if **ok_ty != AbiType::String || **err_ty != AbiType::I32 {
return Err(CodegenError::Unsupported("result out params".to_string()));
return Err(CodegenError::Unsupported(
abi_quirks::result_out_params_error().to_string(),
));
}
Ok(ValueRepr::Result {
tag: *tag,
ok: Box::new(ValueRepr::Pair(ptr, len)),
err: Box::new(ValueRepr::Single(err)),
})
}
_ => Err(CodegenError::Unsupported("result out params".to_string())),
_ => Err(CodegenError::Unsupported(
abi_quirks::result_out_params_error().to_string(),
)),
}
} else if let AbiType::ResultOut(_, _) = &abi_sig.ret {
} else if abi_quirks::is_result_out(&abi_sig.ret) {
let tag = results
.get(0)
.ok_or_else(|| CodegenError::Codegen("missing result tag".to_string()))?;
Expand Down Expand Up @@ -1213,7 +1222,9 @@ fn store_value_by_ty(
return Err(CodegenError::Unsupported("store result".to_string()));
};
let AbiType::Result(ok_abi, err_abi) = &ty.abi else {
return Err(CodegenError::Unsupported("result abi mismatch".to_string()));
return Err(CodegenError::Unsupported(
abi_quirks::result_abi_mismatch_error().to_string(),
));
};
let ok_ty = crate::hir::HirType {
ty: args[0].clone(),
Expand Down Expand Up @@ -1360,7 +1371,9 @@ fn load_value_by_ty(
Ty::Path(name, args) => {
if name == "Result" && args.len() == 2 {
let AbiType::Result(ok_abi, err_abi) = &ty.abi else {
return Err(CodegenError::Unsupported("result abi mismatch".to_string()));
return Err(CodegenError::Unsupported(
abi_quirks::result_abi_mismatch_error().to_string(),
));
};
let ok_ty = crate::hir::HirType {
ty: args[0].clone(),
Expand Down Expand Up @@ -1461,14 +1474,9 @@ fn aligned_slot_size(size: u32, align: u32) -> u32 {
size.max(1).saturating_add(align.saturating_sub(1))
}

/// ResultString ABI uses a u64 length slot across targets.
fn result_string_len_bytes() -> u32 {
8
}

fn result_string_slots(builder: &mut FunctionBuilder, ptr_ty: Type) -> ResultStringSlots {
let ptr_align = ptr_ty.bytes() as u32;
let len_bytes = result_string_len_bytes();
let len_bytes = abi_quirks::result_string_len_bytes();
let len_align = len_bytes;
let err_align = 4u32;
let slot_ptr = builder.create_sized_stack_slot(ir::StackSlotData::new(
Expand Down Expand Up @@ -2144,7 +2152,9 @@ fn value_type_for_result_out(ty: &AbiType, ptr_ty: Type) -> Result<ir::Type, Cod
AbiType::Handle => Ok(ir::types::I64),
AbiType::Ptr => Ok(ptr_ty),
AbiType::Unit => Err(CodegenError::Unsupported("result out unit".to_string())),
_ => Err(CodegenError::Unsupported("result out params".to_string())),
_ => Err(CodegenError::Unsupported(
abi_quirks::result_out_params_error().to_string(),
)),
}
}

Expand Down Expand Up @@ -2175,8 +2185,12 @@ fn zero_value_for_tykind(
err: Box::new(err_val),
})
}
AbiType::ResultOut(_, _) => Err(CodegenError::Unsupported("result out params".to_string())),
AbiType::ResultString => Err(CodegenError::Unsupported("result abi".to_string())),
AbiType::ResultOut(_, _) => Err(CodegenError::Unsupported(
abi_quirks::result_out_params_error().to_string(),
)),
AbiType::ResultString => Err(CodegenError::Unsupported(
abi_quirks::result_string_params_error().to_string(),
)),
}
}

Expand All @@ -2201,7 +2215,9 @@ fn zero_value_for_ty(
Ty::Path(name, args) => {
if name == "Result" && args.len() == 2 {
let AbiType::Result(ok_abi, err_abi) = &ty.abi else {
return Err(CodegenError::Unsupported("result abi mismatch".to_string()));
return Err(CodegenError::Unsupported(
abi_quirks::result_abi_mismatch_error().to_string(),
));
};
let ok_ty = crate::hir::HirType {
ty: args[0].clone(),
Expand Down Expand Up @@ -2309,11 +2325,16 @@ fn value_from_results(
err: Box::new(err_val),
})
}
AbiType::ResultOut(_, _) => Err(CodegenError::Unsupported("result out params".to_string())),
AbiType::ResultString => Err(CodegenError::Unsupported("result abi".to_string())),
AbiType::ResultOut(_, _) => Err(CodegenError::Unsupported(
abi_quirks::result_out_params_error().to_string(),
)),
AbiType::ResultString => Err(CodegenError::Unsupported(
abi_quirks::result_string_params_error().to_string(),
)),
}
}

// --- Runtime call emission ---
/// Emit a call to a runtime intrinsic with ABI adaptation when needed.
pub(super) fn emit_runtime_wrapper_call(
builder: &mut FunctionBuilder,
Expand All @@ -2326,7 +2347,7 @@ pub(super) fn emit_runtime_wrapper_call(
let mut out_slots: Option<ResultStringSlots> = None;
let mut result_out = None;

if abi_sig.ret == AbiType::ResultString {
if abi_quirks::is_result_string(&abi_sig.ret) {
let ptr_ty = module.isa().pointer_type();
let slots = result_string_slots(builder, ptr_ty);
push_result_string_out_params(builder, ptr_ty, &slots, &mut args);
Expand Down Expand Up @@ -2378,7 +2399,7 @@ pub(super) fn emit_runtime_wrapper_call(
let call_inst = builder.ins().call(local, &args);
let results = builder.inst_results(call_inst).to_vec();

if abi_sig.ret == AbiType::ResultString {
if abi_quirks::is_result_string(&abi_sig.ret) {
let tag = results
.get(0)
.ok_or_else(|| CodegenError::Codegen("missing result tag".to_string()))?;
Expand All @@ -2388,19 +2409,23 @@ pub(super) fn emit_runtime_wrapper_call(
match &info.sig.ret {
AbiType::Result(ok_ty, err_ty) => {
if **ok_ty != AbiType::String || **err_ty != AbiType::I32 {
return Err(CodegenError::Unsupported("result out params".to_string()));
return Err(CodegenError::Unsupported(
abi_quirks::result_out_params_error().to_string(),
));
}
return Ok(ValueRepr::Result {
tag: *tag,
ok: Box::new(ValueRepr::Pair(ptr, len)),
err: Box::new(ValueRepr::Single(err)),
});
}
_ => return Err(CodegenError::Unsupported("result out params".to_string())),
_ => return Err(CodegenError::Unsupported(
abi_quirks::result_out_params_error().to_string(),
)),
}
}

if let AbiType::ResultOut(_, _) = &abi_sig.ret {
if abi_quirks::is_result_out(&abi_sig.ret) {
let tag = results
.get(0)
.ok_or_else(|| CodegenError::Codegen("missing result tag".to_string()))?;
Expand Down Expand Up @@ -2447,12 +2472,13 @@ fn ensure_abi_sig_handled(info: &FnInfo) -> Result<(), CodegenError> {
if abi_sig == &info.sig {
return Ok(());
}
match abi_sig.ret {
AbiType::ResultString | AbiType::ResultOut(_, _) => Ok(()),
_ => Err(CodegenError::Codegen(format!(
if abi_quirks::abi_sig_requires_lowering(abi_sig, &info.sig) {
Ok(())
} else {
Err(CodegenError::Codegen(format!(
"abi signature mismatch for {} without ResultString/ResultOut lowering",
info.symbol
))),
)))
}
}

Expand All @@ -2469,7 +2495,7 @@ mod tests {

#[test]
fn result_string_len_is_u64() {
assert_eq!(result_string_len_bytes(), 8);
assert_eq!(abi_quirks::result_string_len_bytes(), 8);
}

#[test]
Expand Down
1 change: 1 addition & 0 deletions capc/src/codegen/intrinsics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
//! Any stdlib function listed here is treated as an intrinsic: its `.cap` body
//! is ignored, and codegen emits a direct call to the runtime symbol. If a
//! function is not listed here, the Capable implementation is used instead.
//! See `stdlib/README.md` for the stdlib-facing explanation.

use std::collections::HashMap;

Expand Down
12 changes: 7 additions & 5 deletions capc/src/codegen/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ use std::collections::{HashMap, HashSet};
use cranelift_codegen::ir::Type;

use super::{
CodegenError, EnumIndex, StructFieldLayout, StructLayout, StructLayoutIndex, TypeLayout,
abi_quirks, CodegenError, EnumIndex, StructFieldLayout, StructLayout, StructLayoutIndex,
TypeLayout,
};
use crate::abi::AbiType;

Expand Down Expand Up @@ -178,9 +179,10 @@ fn type_layout_for_hir_type(
match &ty.ty {
Ty::Path(name, args) if name == "Result" && args.len() == 2 => {
let AbiType::Result(ok_abi, err_abi) = &ty.abi else {
return Err(CodegenError::Unsupported(
"result abi mismatch in layout".to_string(),
));
return Err(CodegenError::Unsupported(format!(
"{} in layout",
abi_quirks::result_abi_mismatch_error()
)));
};
let tag = TypeLayout { size: 1, align: 1 };
let ok = type_layout_for_abi(ok_abi, ptr_ty)?;
Expand Down Expand Up @@ -265,7 +267,7 @@ pub(super) fn type_layout_for_abi(
Ok(TypeLayout { size, align })
}
AbiType::ResultOut(_, _) | AbiType::ResultString => Err(CodegenError::Unsupported(
"layout for result out params".to_string(),
abi_quirks::result_lowering_layout_error().to_string(),
)),
}
}
Expand Down
Loading
Loading