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
22 changes: 18 additions & 4 deletions docs/agents/frontend/wide-floats-assembly.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ x87 and byte-eight bit 63 for binary128. It does not widen or narrow a value
before observing the sign. This preserves signaling NaNs, signed zero and
floating exception state. `basic_c_signbit_images.c` and its independent host
observer cover those images across the native target/mode/frontend/PIC matrix.
AArch64 binary128 widening uses ordinary MIR frame images; see the machine
guide for its exact conversion and native floating-environment checks. This
does not claim binary128 scalar ABI or arithmetic support.
AArch64 binary128 widening and scalar transport use ordinary MIR frame
images; see the machine guide for their exact conversion and ABI-boundary
checks. Arithmetic, comparison, truth conversion and general narrowing remain
separately unsupported.
The host FENV fixture in `tests/host_aarch64_float_to_f128.c` uses ordinary
GNU inline asm for `mrs`/`msr` reads and writes of `fpsr`/`fpcr`; the baseline
AArch64 inline-assembly vocabulary selects these checked system-register rows
Expand Down Expand Up @@ -81,7 +82,7 @@ Read the matching sections; [the frontend index](../frontend.md) lists these not
static BF16 initializers use this path. An explicit cast to `double` still
deliberately rounds to binary64. The binary128 rational converter uses a
two-limb quotient, not a host extended type or a new numeric dependency.
This does not add native BF16 arithmetic/ABI or binary128 scalar ABI support.
This does not add native BF16 arithmetic/ABI or binary128 arithmetic support.

`c_parse_bfloat16_builtin` carries the LLVM18 BF16/AVX-NE-CONVERT signatures
and the select/FMA dependencies used by the pristine resource headers.
Expand All @@ -94,6 +95,19 @@ Read the matching sections; [the frontend index](../frontend.md) lists these not
native intrinsic lowering. `c_test_bfloat16_semantic_acceptance` covers
source-format rounding on six layouts in both frontend forms, mixed-format
identity, positive/negative builtin operands, and deep nested calls.
- **Base AAPCS64 `long double` is IEEE binary128 and supports scalar
transport.** A scalar argument or result is one complete sixteen-byte image
in a Q register; after V0-V7 are exhausted, named arguments occupy their
sixteen-byte-aligned stack slot. Canonical and MIR backends keep the value
slot-backed internally and bridge only at the ABI edges, so assignment,
literal return, direct/indirect calls and mixed Clang/Buster linkage preserve
every payload bit, including negative zero. `c_ir_signature_type_supported`
admits only the exact scalar shape proven by `ir_type_abi_value`; aggregates,
variadic wide parameters, arithmetic, comparisons, truth conversion and
general conversions remain behind their existing structured rejections.
`compiler_driver_test_aarch64_binary128_transport` covers Q0/Q1, ninth-argument stack
spill and both mixed-compiler directions on native Linux AArch64, with strict
no-fallback compilation across the AAPCS64 target/mode/frontend/PIC matrix.
- **`long double` is 80-bit x87 on System V x86-64, and it is memory-only.**
Transport, the four arithmetic operators, negation, the six comparisons,
truth conversion, and the conversions to and from the narrower floats and
Expand Down
8 changes: 4 additions & 4 deletions docs/native-retirement-census.md
Original file line number Diff line number Diff line change
Expand Up @@ -192,13 +192,13 @@ and the project-owned MinGW varargs adapter described below.

The binding values are frozen in both the C producer and the independent
validator: descriptor SHA-256
`514f4611b44211a1184ec5a029f4278b07f8964179f670b44f4cc39dfce1df0d`, materializer
`33be3c1582858afb570298ae49db193293e7ec2008d3a6b85f03df3485dea803`, materializer
receipt SHA-256
`c0f9044a60f987f476353dee297324d9cf0afe4124f078252edd772491a8ad58`, project
`dc14e25a42f9000071d46c776f43282852bcebbf392a91089afe6b7e46aed55d`, project
closure SHA-256
`66bdfb46423045ca112f92e98fb1da013d93e8e6f26a30be50f35205a3a0f8b1`, and
`542c978ad5f8252917fb0fd93cdd318ac8fcca9db14ffa1093edb606a8d637a2`, and
materializer ledger SHA-256
`7f3472bdefc8a998c935a972786b82862e2c3bb8b7f66dec6504ce055df69ffa`.
`fa98a21ebeeede091e8810034b315d12c66f629ba2d5e2c4225b5f96fc1ce48a`.
The archived fixture-input map is
`bef841ade0921ffe9293440171b1d0d8dd6c3cf798f2535d8790b4ad26542500`, the
fixture-to-project-header map is
Expand Down
4 changes: 2 additions & 2 deletions docs/native-retirement-dependencies-v1.json
Original file line number Diff line number Diff line change
Expand Up @@ -492,8 +492,8 @@
"source": "src/buster/lib/compiler/ir/ir.c",
"provenance": "repo:src/buster/lib/compiler/ir/ir.c",
"destination": "dependencies/project-include/buster/lib/compiler/ir/ir.c",
"bytes": 269075,
"sha256": "1b9df7a0af76ff524477dc84ee3cab09903e556b58303b6c77e8cd5a83d67518"
"bytes": 270100,
"sha256": "1507193c55bbb96e075ecdf6d3c26dd053ef0fedb89b97156b7058acb40b1b21"
},
{
"source": "src/buster/lib/compiler/ir/ir.h",
Expand Down
12 changes: 8 additions & 4 deletions src/buster/lib/compiler/codegen/codegen.c
Original file line number Diff line number Diff line change
Expand Up @@ -20449,7 +20449,8 @@ BUSTER_GLOBAL_LOCAL CodegenModule codegen_generate_canonical_module_attempt(Aren
}
if (argument_type->kind == IR_TYPE_FLOAT && !aarch64_windows_variadic)
{
if (!codegen_canonical_type_is_ieee_binary16(argument_type) && argument_type->bit_width != 32 && argument_type->bit_width != 64)
if (!codegen_canonical_type_is_ieee_binary16(argument_type) && argument_type->bit_width != 32 &&
argument_type->bit_width != 64 && argument_type->bit_width != 128)
{
result.error = CODEGEN_ERROR_UNSUPPORTED_ABI;
return result;
Expand Down Expand Up @@ -22204,7 +22205,8 @@ BUSTER_GLOBAL_LOCAL CodegenModule codegen_generate_canonical_module_attempt(Aren
{
u8 float_register = argument_float_register[argument_index - 1];
if (float_register >= 8 ||
(!codegen_canonical_type_is_ieee_binary16(argument_type) && argument_type->bit_width != 32 && argument_type->bit_width != 64))
(!codegen_canonical_type_is_ieee_binary16(argument_type) && argument_type->bit_width != 32 &&
argument_type->bit_width != 64 && argument_type->bit_width != 128))
{
result.error = CODEGEN_ERROR_UNSUPPORTED_ABI;
return result;
Expand Down Expand Up @@ -22300,7 +22302,8 @@ BUSTER_GLOBAL_LOCAL CodegenModule codegen_generate_canonical_module_attempt(Aren
}
if (return_type && return_type->kind == IR_TYPE_FLOAT)
{
if (!codegen_canonical_type_is_ieee_binary16(return_type) && return_type->bit_width != 32 && return_type->bit_width != 64)
if (!codegen_canonical_type_is_ieee_binary16(return_type) && return_type->bit_width != 32 &&
return_type->bit_width != 64 && return_type->bit_width != 128)
{
result.error = CODEGEN_ERROR_UNSUPPORTED_ABI;
return result;
Expand Down Expand Up @@ -23393,7 +23396,8 @@ BUSTER_GLOBAL_LOCAL CodegenModule codegen_generate_canonical_module_attempt(Aren
}
else if (return_type && return_type->kind == IR_TYPE_FLOAT)
{
if (!codegen_canonical_type_is_ieee_binary16(return_type) && return_type->bit_width != 32 && return_type->bit_width != 64)
if (!codegen_canonical_type_is_ieee_binary16(return_type) && return_type->bit_width != 32 &&
return_type->bit_width != 64 && return_type->bit_width != 128)
{
result.error = CODEGEN_ERROR_UNSUPPORTED_ABI;
return result;
Expand Down
47 changes: 36 additions & 11 deletions src/buster/lib/compiler/codegen/machine_aarch64.c
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,25 @@ BUSTER_GLOBAL_LOCAL bool machine_a64_value_shape(IrProgram* program, IrTypeId ty
};
return true;
}
if (type && type->layout.resolved && type->kind == IR_TYPE_FLOAT && type->bit_width == 128 && type->layout.size == 16 &&
ir_abi_convention_for_target(target) == IR_ABI_CONVENTION_AAPCS64)
{
// Binary128 is an ordinary sixteen-byte Q-register image at AAPCS64
// boundaries, but the machine IR keeps it in a complete stack slot.
// Aggregate-like placement also preserves the full aligned stack image
// after the eight vector argument registers are exhausted.
*shape = (MachineA64ValueShape){
.part_is_float = {1},
.part_sizes = {16},
.part_count = 1,
.byte_size = 16,
.exact_byte_size = 16,
.aggregate = true,
.vector = true,
.stack_aligned = true,
};
return true;
}
// A bare 128-bit integer is the AAPCS64 even-aligned X pair — the same
// two INTEGER parts a sixteen-byte wrapped pair builds — carried by the
// aggregate machinery over the value's 16-byte slot, exactly like the
Expand Down Expand Up @@ -978,19 +997,24 @@ BUSTER_GLOBAL_LOCAL bool machine_a64_select_constant(MachineA64Selector* selecto
machine_a64_define(selector, result_register, row);
selected = true;
}
else if (instruction->opcode == IR_OPCODE_CONSTANT_INTEGER && instruction->result.value != IR_ID_UNDERLYING_INVALID &&
instruction->result.value < function->value_count && selector->value_stack_slots[instruction->result.value] != UINT32_MAX)
else if ((instruction->opcode == IR_OPCODE_CONSTANT_INTEGER || instruction->opcode == IR_OPCODE_CONSTANT_FLOAT) &&
instruction->result.value != IR_ID_UNDERLYING_INVALID && instruction->result.value < function->value_count &&
selector->value_stack_slots[instruction->result.value] != UINT32_MAX)
{
// A 128-bit integer constant is slot-backed like every i128 value.
// The second eightbyte is the canonical emitters' selection: an
// explicit second immediate when the frontend recorded one,
// otherwise the negated low half's sign fill.
// Wide scalar constants are slot-backed like every i128/f128 value.
// Integer constants retain their signed-immediate convention; a
// binary128 floating constant already carries its exact two target
// image limbs in canonical low/high order.
IrType* constant_type = ir_type_from_id(&program->types, instruction->canonical_type);
if (constant_type && constant_type->kind == IR_TYPE_INTEGER && constant_type->bit_width == 128)
bool integer128 = constant_type && constant_type->kind == IR_TYPE_INTEGER && constant_type->bit_width == 128;
bool float128 = constant_type && constant_type->kind == IR_TYPE_FLOAT && constant_type->bit_width == 128 &&
instruction->immediate_count == 2;
if (integer128 || float128)
{
u32 result_slot = selector->value_stack_slots[instruction->result.value];
u64 low = instruction->immediate_is_negative ? 0 - instruction->immediates[0] : instruction->immediates[0];
u64 high = instruction->immediate_count > 1 ? instruction->immediates[1] : instruction->immediate_is_negative ? UINT64_MAX : 0;
u64 low = integer128 && instruction->immediate_is_negative ? 0 - instruction->immediates[0] : instruction->immediates[0];
u64 high = float128 || instruction->immediate_count > 1 ? instruction->immediates[1]
: instruction->immediate_is_negative ? UINT64_MAX : 0;
u64 halves[2] = {low, high};
for (u32 half_index = 0; half_index < 2; half_index += 1)
{
Expand Down Expand Up @@ -6482,7 +6506,7 @@ MachineSelectResult machine_select_canonical_function_aarch64(Arena* arena, IrPr
instruction->opcode == IR_OPCODE_ATOMIC_COMPARE_EXCHANGE || instruction->opcode == IR_OPCODE_CALL ||
instruction->opcode == IR_OPCODE_AGGREGATE || instruction->opcode == IR_OPCODE_ARRAY ||
instruction->opcode == IR_OPCODE_VA_ARG || instruction->opcode == IR_OPCODE_CAST ||
instruction->opcode == IR_OPCODE_CONSTANT_INTEGER ||
instruction->opcode == IR_OPCODE_CONSTANT_INTEGER || instruction->opcode == IR_OPCODE_CONSTANT_FLOAT ||
((instruction->opcode == IR_OPCODE_BINARY || instruction->opcode == IR_OPCODE_UNARY) && value_type &&
(value_type->kind == IR_TYPE_VECTOR || (value_type->kind == IR_TYPE_INTEGER && value_type->bit_width == 128)))) &&
value_type && value_type->layout.resolved && value_type->layout.size <= UINT32_MAX - 7 &&
Expand All @@ -6503,8 +6527,9 @@ MachineSelectResult machine_select_canonical_function_aarch64(Arena* arena, IrPr
{
slot_size = 16;
}
bool wide_float = value_type->kind == IR_TYPE_FLOAT && value_type->bit_width == 128;
selector.value_stack_slots[instruction->result.value] = machine_a64_append_slot(
&selector, slot_size, value_type->kind == IR_TYPE_VECTOR ? 16u : 8u);
&selector, slot_size, value_type->kind == IR_TYPE_VECTOR || wide_float ? 16u : 8u);
}
}
}
Expand Down
67 changes: 64 additions & 3 deletions src/buster/lib/compiler/frontend/c/c_gen.c
Original file line number Diff line number Diff line change
Expand Up @@ -1221,6 +1221,18 @@ BUSTER_C_INTERNAL bool c_ir_target_supports_f80(Target target)
layout.long_double_type.alignment == 16;
}

// Base AAPCS64 long double is IEEE binary128. This predicate deliberately
// admits only the exact scalar representation whose shared ABI classification
// is one sixteen-byte vector-file part; arithmetic and conversions remain
// independently gated by their lowering paths.
BUSTER_C_INTERNAL bool c_ir_target_supports_f128_transport(Target target)
{
TargetDataLayout layout = target_data_layout(target);
return target.cpu_arch == CPU_ARCH_AARCH64 && ir_abi_convention_for_target(target) == IR_ABI_CONVENTION_AAPCS64 &&
layout.endianness == TARGET_ENDIAN_LITTLE && layout.long_double_type.bit_width == 128 &&
layout.long_double_type.size == 16 && layout.long_double_type.alignment == 16;
}

// A wide value is safe for the canonical x86 backend only when the existing
// SysV classifier proves the complete value is the two-part x87 return shape.
// This intentionally asks the classifier rather than walking fields here:
Expand Down Expand Up @@ -1304,7 +1316,14 @@ BUSTER_C_INTERNAL bool c_ir_signature_type_supported(IrProgram* program, CIrWide
IrAbiValue abi = ir_type_abi_value(program, type_id, convention, result_type ? IR_ABI_USE_RESULT : IR_ABI_USE_ARGUMENT);
if (c_ir_type_contains_wide_float(program, wide_float_cache, type_id))
{
if (c_ir_type_is_f80_x87_shape(program, wide_float_cache, type_id, target))
if (c_ir_target_supports_f128_transport(target) && type->kind == IR_TYPE_FLOAT && type->bit_width == 128 && !type->is_atomic)
{
if (abi.memory || abi.indirect || abi.part_count != 1 || abi.parts[0].abi_class != IR_ABI_CLASS_VECTOR || abi.parts[0].size != 16)
{
return false;
}
}
else if (c_ir_type_is_f80_x87_shape(program, wide_float_cache, type_id, target))
{
// SysV passes both scalar f80 and ABI-proven wrappers by value in
// a sixteen-byte stack slot. Results come back as the x87 pair.
Expand Down Expand Up @@ -11066,6 +11085,28 @@ BUSTER_C_INTERNAL IrValueId c_ir_emit_f80_constant_bits(CIntegerIrBuilder* build
return result;
}

BUSTER_C_INTERNAL IrValueId c_ir_emit_f128_constant_bits(CIntegerIrBuilder* builder, IrSourceRange source, String8 literal, u64 low, u64 high)
{
IrValueId result = IR_VALUE_ID_INVALID;
IrType* type = ir_type_from_id(&builder->program->types, builder->long_double_type);
if (type && c_ir_target_supports_f128_transport(builder->target) && type->kind == IR_TYPE_FLOAT && type->bit_width == 128 &&
type->layout.size == 16 && type->layout.alignment == 16)
{
result = c_ir_add_result(builder, builder->long_double_type);
u64* immediate = arena_allocate(builder->arena, u64, 2);
immediate[0] = low;
immediate[1] = high;
IrInstruction instruction = c_ir_instruction_initialize(IR_OPCODE_CONSTANT_FLOAT, builder->long_double_type);
instruction.immediates = immediate;
instruction.immediate_count = 2;
instruction.result = result;
IrInstructionId id = c_ir_append_instruction(builder, instruction, source);
ir_instruction_extra_ensure(builder->arena, builder->function, id)->literal = literal;
builder->function->values[result.value].definition = id;
}
return result;
}

BUSTER_C_INTERNAL IrValueId c_ir_emit_float_spelling(CIntegerIrBuilder* builder, String8 spelling, IrSourceRange source)
{
char8 suffix = 0;
Expand Down Expand Up @@ -11104,6 +11145,16 @@ BUSTER_C_INTERNAL IrValueId c_ir_emit_float_spelling(CIntegerIrBuilder* builder,
}
return c_ir_emit_f80_constant_bits(builder, source, spelling, significand, sign_exponent);
}
if (type_value->bit_width == 128 && c_ir_target_supports_f128_transport(builder->target))
{
CIrConstantValue constant = {0};
IrValueId result = IR_VALUE_ID_INVALID;
if (c_ir_constant_float_literal(builder, spelling, &constant) && constant.kind == C_IR_CONSTANT_FLOAT)
{
result = c_ir_emit_f128_constant_bits(builder, source, spelling, constant.integer, constant.integer_high);
}
return result;
}
if (type_value->bit_width > 64)
{
return IR_VALUE_ID_INVALID;
Expand Down Expand Up @@ -22462,8 +22513,18 @@ BUSTER_C_INTERNAL bool c_ir_apply_operation(CIntegerIrBuilder* builder, CConditi
IrInstruction* constant = builder->function->instructions + definition.value;
if (constant->opcode == IR_OPCODE_CONSTANT_FLOAT && constant->immediate_count == 2 && constant->immediates)
{
IrValueId negated = c_ir_emit_f80_constant_bits(builder, source, ir_instruction_extra(builder->function, definition).literal,
constant->immediates[0], (u16)(constant->immediates[1] ^ UINT64_C(0x8000)));
IrType* constant_type = ir_type_from_id(&builder->program->types, constant->canonical_type);
IrValueId negated = IR_VALUE_ID_INVALID;
if (constant_type && constant_type->bit_width == 80)
{
negated = c_ir_emit_f80_constant_bits(builder, source, ir_instruction_extra(builder->function, definition).literal,
constant->immediates[0], (u16)(constant->immediates[1] ^ UINT64_C(0x8000)));
}
else if (constant_type && constant_type->bit_width == 128)
{
negated = c_ir_emit_f128_constant_bits(builder, source, ir_instruction_extra(builder->function, definition).literal,
constant->immediates[0], constant->immediates[1] ^ UINT64_C(0x8000000000000000));
}
if (negated.value != IR_ID_UNDERLYING_INVALID)
{
*value_count = first;
Expand Down
Loading
Loading