diff --git a/src/astgen.cpp b/src/astgen.cpp index 450dd0c..8335e22 100644 --- a/src/astgen.cpp +++ b/src/astgen.cpp @@ -2085,9 +2085,9 @@ static JirRef astgenAsCast(AstGenCtx &gctx, const AstNode &n) { // numeric literal operand settles directly at the target width. For // floats this is essential: ` as f32` must round the literal to f32 // in one step, not lower it at f64 and then FPTrunc (which double-rounds). - TypeIdx hint = - (dst.kind == TypeKind::Int || dst.kind == TypeKind::Float) ? dstTy - : kNoType; + TypeIdx hint = (dst.kind == TypeKind::Int || dst.kind == TypeKind::Float) + ? dstTy + : kNoType; JirRef val = astgenExpr(gctx, operandIdx, hint); TypeIdx srcTy = gctx.jfn.getInst(val).ty; if (srcTy == dstTy) return val; diff --git a/src/comptime.cpp b/src/comptime.cpp index 76036cb..b0b18b1 100644 --- a/src/comptime.cpp +++ b/src/comptime.cpp @@ -218,14 +218,15 @@ ComptimeValue ComptimeEvaluator::evalNumberLit(const AstNode &n) const { bool isFloat = (n.flags & 2) != 0; if (isFloat) { // The comptime float value is f64. Either the literal is stored inline - // as f64 bits (lossless), or as full f128 in the extra pool (flag bit 2) - // which we round once to f64 (f128→f64 equals decimal→f64; no double- - // rounding). See the parser / astgenNumberLit. + // as f64 bits (lossless), or as full f128 in the extra pool (flag bit + // 2) which we round once to f64 (f128→f64 equals decimal→f64; no + // double- rounding). See the parser / astgenNumberLit. double v; if ((n.flags & 4) != 0) { ExtraIdx ei = static_cast(n.lhs); uint32_t quad[4] = {nodes_.getExtra(ei), nodes_.getExtra(ei + 1), - nodes_.getExtra(ei + 2), nodes_.getExtra(ei + 3)}; + nodes_.getExtra(ei + 2), + nodes_.getExtra(ei + 3)}; v = JamLLVMQuadToTargetAsDouble(quad, /*toF32=*/false); } else { uint64_t bits = static_cast(n.lhs) | diff --git a/src/jam_llvm.cpp b/src/jam_llvm.cpp index 5de2fcf..a731754 100644 --- a/src/jam_llvm.cpp +++ b/src/jam_llvm.cpp @@ -7,13 +7,13 @@ #include "jam_llvm.h" +#include "llvm/ADT/APFloat.h" +#include "llvm/ADT/APInt.h" +#include "llvm/ADT/ArrayRef.h" #include "llvm/Analysis/AliasAnalysis.h" #include "llvm/Analysis/CGSCCPassManager.h" #include "llvm/Analysis/LoopAnalysisManager.h" #include "llvm/Analysis/TargetLibraryInfo.h" -#include "llvm/ADT/APFloat.h" -#include "llvm/ADT/APInt.h" -#include "llvm/ADT/ArrayRef.h" #include "llvm/Bitcode/BitcodeWriter.h" #include "llvm/IR/Constants.h" #include "llvm/IR/DerivedTypes.h" @@ -290,7 +290,7 @@ JamValueRef JamLLVMConstReal(JamTypeRef type, double val) { } bool JamLLVMParseDecimalFloat(const char *str, unsigned len, uint64_t *outF64, - uint32_t *outQuad) { + uint32_t *outQuad) { // Parse the decimal/hex float text into IEEE binary128 with one correctly // rounded step (APFloat is arbitrary-precision internally — this is the // path clang uses for float literals). f128 is wide enough that a later @@ -307,14 +307,15 @@ bool JamLLVMParseDecimalFloat(const char *str, unsigned len, uint64_t *outF64, // compact f64 form instead of the full f128. llvm::APFloat asF64 = q; bool lostInfo = false; - asF64.convert(llvm::APFloat::IEEEdouble(), llvm::APFloat::rmNearestTiesToEven, - &lostInfo); + asF64.convert(llvm::APFloat::IEEEdouble(), + llvm::APFloat::rmNearestTiesToEven, &lostInfo); if (!lostInfo) { *outF64 = asF64.bitcastToAPInt().getZExtValue(); return false; // fits f64 losslessly — caller stores f64 inline } llvm::APInt bits = q.bitcastToAPInt(); // 128-bit pattern - const uint64_t *raw = bits.getRawData(); // raw[0] = low 64, raw[1] = high 64 + const uint64_t *raw = + bits.getRawData(); // raw[0] = low 64, raw[1] = high 64 outQuad[0] = static_cast(raw[0] & 0xFFFFFFFFu); outQuad[1] = static_cast(raw[0] >> 32); outQuad[2] = static_cast(raw[1] & 0xFFFFFFFFu); diff --git a/src/jam_llvm.h b/src/jam_llvm.h index cfae12d..7c3ef07 100644 --- a/src/jam_llvm.h +++ b/src/jam_llvm.h @@ -147,9 +147,9 @@ JAM_EXTERN_C JamValueRef JamLLVMConstReal(JamTypeRef type, double val); // to its target exactly once, never decimal→f64→f32. JAM_EXTERN_C bool JamLLVMParseDecimalFloat(const char *str, unsigned len, uint64_t *outF64, uint32_t *outQuad); -// Round an f128 value (the 4×u32 pattern above) once to f32 (toF32=true) or f64, -// returned widened to a C double (an f32 result is exact in f64). This is the -// single, final rounding — no double-rounding through f64. +// Round an f128 value (the 4×u32 pattern above) once to f32 (toF32=true) or +// f64, returned widened to a C double (an f32 result is exact in f64). This is +// the single, final rounding — no double-rounding through f64. JAM_EXTERN_C double JamLLVMQuadToTargetAsDouble(const uint32_t *quad, bool toF32); JAM_EXTERN_C JamValueRef JamLLVMConstNull(JamTypeRef type); diff --git a/src/jir_codegen.cpp b/src/jir_codegen.cpp index 776aab7..10e7957 100644 --- a/src/jir_codegen.cpp +++ b/src/jir_codegen.cpp @@ -574,6 +574,24 @@ static JamValueRef emitInstImpl(JirCodegenCtx &lctx, JirRef r) { JamTypeRef baseLLVMTy = lctx.ctx.getLLVMType(baseInst.ty); JamTypeRef elemLLVMTy = lctx.ctx.getLLVMType(inst.ty); + // Byref aggregate element types (Struct / Array / Union / payloaded + // Enum) follow the same convention as JirTag::Load above: the + // JirRef VALUE is the storage pointer, never a materialized + // aggregate in SSA. Skip the trailing `load %T, ptr %gep`; + // downstream Store / Ret / arg-passing then sees a byref pointer + // and emits memcpy / pointer-forward. Mirrors the byref branch in + // Zig's airSliceElemVal / airPtrElemVal / airArrayElemVal + // (references/zig-0.10.1 src/codegen/llvm.zig ~5678/5716/5745): + // they do the same GEP + return-pointer-for-byref-elem. jam's + // universal "byref JirRef = pointer" invariant means we don't + // need Zig's `loadByRef` fallback — every downstream byref + // consumer (Store/Ret/Call) already memcpy's from the pointer. + // Without this guard the aggregate was loaded into SSA, but the + // byref consumer still treated the JirRef as a pointer — the + // result was a malformed memcpy passing a struct value where a + // src pointer was expected. Byval elements (scalars / pointers / + // slices) keep the GEP+Load path. + const bool elemByRef = jam::abi::isByRef(inst.ty, lctx.ctx); if (basek.kind == TypeKind::Slice) { // SSA slice value: extract the pointer field (0), GEP by elem. JamValueRef base = emitInst(lctx, inst.a); @@ -581,6 +599,7 @@ static JamValueRef emitInstImpl(JirCodegenCtx &lctx, JirRef r) { base, 0, "slice.ptr"); JamValueRef gep = JamLLVMBuildPtrGEP( lctx.ctx.getBuilder(), elemLLVMTy, ptr, idxVal, "idx.gep"); + if (elemByRef) { return gep; } return JamLLVMBuildLoad(lctx.ctx.getBuilder(), elemLLVMTy, gep, "idx"); } @@ -588,6 +607,7 @@ static JamValueRef emitInstImpl(JirCodegenCtx &lctx, JirRef r) { JamValueRef base = emitInst(lctx, inst.a); JamValueRef gep = JamLLVMBuildPtrGEP( lctx.ctx.getBuilder(), elemLLVMTy, base, idxVal, "idx.gep"); + if (elemByRef) { return gep; } return JamLLVMBuildLoad(lctx.ctx.getBuilder(), elemLLVMTy, gep, "idx"); } @@ -613,6 +633,7 @@ static JamValueRef emitInstImpl(JirCodegenCtx &lctx, JirRef r) { } JamValueRef gep = JamLLVMBuildArrayGEP( lctx.ctx.getBuilder(), baseLLVMTy, storage, idxVal, "idx.gep"); + if (elemByRef) { return gep; } return JamLLVMBuildLoad(lctx.ctx.getBuilder(), elemLLVMTy, gep, "idx"); } case JirTag::AddrOf: {