diff --git a/examples/102-foreign-global-from-helper.sx b/examples/102-foreign-global-from-helper.sx new file mode 100644 index 0000000..00b1d18 --- /dev/null +++ b/examples/102-foreign-global-from-helper.sx @@ -0,0 +1,28 @@ +// `xx @` round-trips through a non-main helper +// function: the helper's `xx @__stdinp` cast lowers to a `bitcast` +// IR opcode that emit_llvm.zig dispatches to `LLVMBuildPtrToInt` +// (BitCast doesn't accept ptr↔int on modern LLVM with opaque +// pointers), and a `u64`-returning function correctly returns +// the address. +// +// Was issue-0037 — the helper used to emit `ret i64 undef` because +// `coerceToType` had no pointer↔integer case, so the explicit +// `xx ptr` cast produced an unchanged pointer value handed to a +// `ret i64` slot. + +#import "modules/std.sx"; + +__stdinp : *void #foreign; + +stdinp_addr_via_helper :: () -> u64 { + xx @__stdinp; +} + +main :: () -> s32 { + direct : u64 = xx @__stdinp; + via_helper := stdinp_addr_via_helper(); + print("direct non-null = {}\n", direct != 0); + print("helper non-null = {}\n", via_helper != 0); + print("eq = {}\n", direct == via_helper); + 0; +} diff --git a/examples/issue-0037.sx b/examples/issue-0037.sx deleted file mode 100644 index 5701859..0000000 --- a/examples/issue-0037.sx +++ /dev/null @@ -1,47 +0,0 @@ -// `@__stdinp` (address-of a `#foreign` global) lowers to `undef` / -// 0 when accessed from inside a non-main function body. From `main` -// directly it works: -// -// single file: -// __stdinp : *void #foreign; -// stdinp_addr :: () -> u64 { xx @__stdinp; } -// main :: () -> s32 { -// a : u64 = xx @__stdinp; // a = -// b := stdinp_addr(); // b = 0 -// ... -// } -// -// The emitted IR for the helper is just `ret i64 undef`, suggesting -// the `address_of(identifier=__stdinp)` branch in `lower.zig` -// (~line 1719) doesn't see `__stdinp` in `global_names` at the time -// the helper's body is being lowered — even though the same lookup -// succeeds inside main. -// -// Likely cause: lazy/lazy-deferred body lowering ordering vs. the -// pass that registers extern global decls into `global_names`. Or -// global_names is scoped per-function (?). Worth verifying which -// before fixing. -// -// Expected when fixed: the helper returns the same address as the -// direct read, prints `eq = true`. Today: prints `eq = false`. -// -// Filed during PLAN-FFI step 0.10 (cross-file `#foreign` global — -// the cross-file dimension surfaced the bug, but it reproduces in -// a single file too). - -#import "modules/std.sx"; - -__stdinp : *void #foreign; - -stdinp_addr_via_helper :: () -> u64 { - xx @__stdinp; -} - -main :: () -> s32 { - direct : u64 = xx @__stdinp; - via_helper := stdinp_addr_via_helper(); - print("direct non-null = {}\n", direct != 0); - print("helper non-null = {}\n", via_helper != 0); - print("eq = {}\n", direct == via_helper); - 0; -} diff --git a/src/ir/emit_llvm.zig b/src/ir/emit_llvm.zig index 34e4ad0..ad7cc10 100644 --- a/src/ir/emit_llvm.zig +++ b/src/ir/emit_llvm.zig @@ -1006,7 +1006,26 @@ pub const LLVMEmitter = struct { .bitcast => |conv| { const operand = self.resolveRef(conv.operand); const to_ty = self.toLLVMType(conv.to); - self.mapRef(c.LLVMBuildBitCast(self.builder, operand, to_ty, "bitcast")); + // LLVMBuildBitCast doesn't accept ptr↔int on modern + // LLVM. Dispatch to PtrToInt / IntToPtr when needed — + // lower.zig emits a `bitcast` IR op for both shapes. + const from_kind = c.LLVMGetTypeKind(c.LLVMTypeOf(operand)); + const to_kind = c.LLVMGetTypeKind(to_ty); + if (from_kind == c.LLVMPointerTypeKind and to_kind == c.LLVMIntegerTypeKind) { + const i64_val = c.LLVMBuildPtrToInt(self.builder, operand, self.cached_i64, "pti"); + const w = c.LLVMGetIntTypeWidth(to_ty); + if (w == 64) { + self.mapRef(i64_val); + } else if (w < 64) { + self.mapRef(c.LLVMBuildTrunc(self.builder, i64_val, to_ty, "pti.tr")); + } else { + self.mapRef(c.LLVMBuildZExt(self.builder, i64_val, to_ty, "pti.ext")); + } + } else if (from_kind == c.LLVMIntegerTypeKind and to_kind == c.LLVMPointerTypeKind) { + self.mapRef(c.LLVMBuildIntToPtr(self.builder, operand, to_ty, "itp")); + } else { + self.mapRef(c.LLVMBuildBitCast(self.builder, operand, to_ty, "bitcast")); + } }, .int_to_float => |conv| { const operand = self.resolveRef(conv.operand); diff --git a/src/ir/lower.zig b/src/ir/lower.zig index 34a033b..f98a4e2 100644 --- a/src/ir/lower.zig +++ b/src/ir/lower.zig @@ -9290,6 +9290,8 @@ pub const Lowering = struct { const dst_float = isFloat(dst_ty); const src_int = self.isIntEx(src_ty); const dst_int = self.isIntEx(dst_ty); + const src_ptr = !src_ty.isBuiltin() and self.module.types.get(src_ty) == .pointer; + const dst_ptr = !dst_ty.isBuiltin() and self.module.types.get(dst_ty) == .pointer; // Int → Float if (src_int and dst_float) { @@ -9299,6 +9301,13 @@ pub const Lowering = struct { if (src_float and dst_int) { return self.builder.emit(.{ .float_to_int = .{ .operand = val, .from = src_ty, .to = dst_ty } }, dst_ty); } + // Ptr ↔ Int — explicit `xx ptr` to/from an integer-typed slot. + // Emits a `bitcast` IR op; emit_llvm.zig's bitcast arm dispatches + // to LLVMBuildPtrToInt / LLVMBuildIntToPtr at the LLVM level + // since LLVMBuildBitCast itself doesn't accept ptr↔int. + if ((src_ptr and dst_int) or (src_int and dst_ptr)) { + return self.builder.emit(.{ .bitcast = .{ .operand = val, .from = src_ty, .to = dst_ty } }, dst_ty); + } // Same kind — widen/narrow based on bit width const src_bits = self.typeBitsEx(src_ty); const dst_bits = self.typeBitsEx(dst_ty); diff --git a/tests/expected/issue-0037.exit b/tests/expected/102-foreign-global-from-helper.exit similarity index 100% rename from tests/expected/issue-0037.exit rename to tests/expected/102-foreign-global-from-helper.exit diff --git a/tests/expected/102-foreign-global-from-helper.txt b/tests/expected/102-foreign-global-from-helper.txt new file mode 100644 index 0000000..751fb3a --- /dev/null +++ b/tests/expected/102-foreign-global-from-helper.txt @@ -0,0 +1,3 @@ +direct non-null = true +helper non-null = true +eq = true diff --git a/tests/expected/ffi-objc-call-03-selector-sharing.ir b/tests/expected/ffi-objc-call-03-selector-sharing.ir index b85ee33..eacbbe8 100644 --- a/tests/expected/ffi-objc-call-03-selector-sharing.ir +++ b/tests/expected/ffi-objc-call-03-selector-sharing.ir @@ -107,7 +107,8 @@ entry: store { ptr, i64 } undef, ptr %allocaN, align 8 %loadN = load ptr, ptr %allocaN, align 8 %gep = getelementptr inbounds { ptr, i64 }, ptr %allocaN, i32 0, i32 0 - store ptr %loadN, ptr %gep, align 8 + %pti = ptrtoint ptr %loadN to i64 + store i64 %pti, ptr %gep, align 8 %loadN = load i64, ptr %alloca, align 8 %gepN = getelementptr inbounds { ptr, i64 }, ptr %allocaN, i32 0, i32 1 store i64 %loadN, ptr %gepN, align 8 diff --git a/tests/expected/ffi-objc-call-06-sret-return.ir b/tests/expected/ffi-objc-call-06-sret-return.ir index 7ab9a67..679c1ab 100644 --- a/tests/expected/ffi-objc-call-06-sret-return.ir +++ b/tests/expected/ffi-objc-call-06-sret-return.ir @@ -267,7 +267,8 @@ entry: store { ptr, i64 } undef, ptr %allocaN, align 8 %loadN = load ptr, ptr %allocaN, align 8 %gep = getelementptr inbounds { ptr, i64 }, ptr %allocaN, i32 0, i32 0 - store ptr %loadN, ptr %gep, align 8 + %pti = ptrtoint ptr %loadN to i64 + store i64 %pti, ptr %gep, align 8 %loadN = load i64, ptr %alloca, align 8 %gepN = getelementptr inbounds { ptr, i64 }, ptr %allocaN, i32 0, i32 1 store i64 %loadN, ptr %gepN, align 8 @@ -2371,7 +2372,8 @@ entry: store ptr %0, ptr %alloca, align 8 %allocaN = alloca i64, align 8 %load = load ptr, ptr %alloca, align 8 - store ptr %load, ptr %allocaN, align 8 + %pti = ptrtoint ptr %load to i64 + store i64 %pti, ptr %allocaN, align 8 %loadN = load i64, ptr %allocaN, align 8 %icmp = icmp eq i64 %loadN, 0 br i1 %icmp, label %if.then.139, label %if.else.140 @@ -2398,7 +2400,8 @@ entry: store ptr %0, ptr %alloca, align 8 %allocaN = alloca i64, align 8 %load = load ptr, ptr %alloca, align 8 - store ptr %load, ptr %allocaN, align 8 + %pti = ptrtoint ptr %load to i64 + store i64 %pti, ptr %allocaN, align 8 %loadN = load i64, ptr %allocaN, align 8 %icmp = icmp eq i64 %loadN, 0 br i1 %icmp, label %if.then.163, label %if.else.164 @@ -2425,7 +2428,8 @@ entry: store ptr %0, ptr %alloca, align 8 %allocaN = alloca i64, align 8 %load = load ptr, ptr %alloca, align 8 - store ptr %load, ptr %allocaN, align 8 + %pti = ptrtoint ptr %load to i64 + store i64 %pti, ptr %allocaN, align 8 %loadN = load i64, ptr %allocaN, align 8 %icmp = icmp eq i64 %loadN, 0 br i1 %icmp, label %if.then.166, label %if.else.167 @@ -2452,7 +2456,8 @@ entry: store ptr %0, ptr %alloca, align 8 %allocaN = alloca i64, align 8 %load = load ptr, ptr %alloca, align 8 - store ptr %load, ptr %allocaN, align 8 + %pti = ptrtoint ptr %load to i64 + store i64 %pti, ptr %allocaN, align 8 %loadN = load i64, ptr %allocaN, align 8 %icmp = icmp eq i64 %loadN, 0 br i1 %icmp, label %if.then.169, label %if.else.170 @@ -2506,7 +2511,8 @@ entry: store ptr %0, ptr %alloca, align 8 %allocaN = alloca i64, align 8 %load = load ptr, ptr %alloca, align 8 - store ptr %load, ptr %allocaN, align 8 + %pti = ptrtoint ptr %load to i64 + store i64 %pti, ptr %allocaN, align 8 %loadN = load i64, ptr %allocaN, align 8 %icmp = icmp eq i64 %loadN, 0 br i1 %icmp, label %if.then.175, label %if.else.176 @@ -2533,7 +2539,8 @@ entry: store ptr %0, ptr %alloca, align 8 %allocaN = alloca i64, align 8 %load = load ptr, ptr %alloca, align 8 - store ptr %load, ptr %allocaN, align 8 + %pti = ptrtoint ptr %load to i64 + store i64 %pti, ptr %allocaN, align 8 %loadN = load i64, ptr %allocaN, align 8 %icmp = icmp eq i64 %loadN, 0 br i1 %icmp, label %if.then.178, label %if.else.179 @@ -2560,7 +2567,8 @@ entry: store ptr %0, ptr %alloca, align 8 %allocaN = alloca i64, align 8 %load = load ptr, ptr %alloca, align 8 - store ptr %load, ptr %allocaN, align 8 + %pti = ptrtoint ptr %load to i64 + store i64 %pti, ptr %allocaN, align 8 %loadN = load i64, ptr %allocaN, align 8 %icmp = icmp eq i64 %loadN, 0 br i1 %icmp, label %if.then.181, label %if.else.182 @@ -2587,7 +2595,8 @@ entry: store ptr %0, ptr %alloca, align 8 %allocaN = alloca i64, align 8 %load = load ptr, ptr %alloca, align 8 - store ptr %load, ptr %allocaN, align 8 + %pti = ptrtoint ptr %load to i64 + store i64 %pti, ptr %allocaN, align 8 %loadN = load i64, ptr %allocaN, align 8 %icmp = icmp eq i64 %loadN, 0 br i1 %icmp, label %if.then.184, label %if.else.185 @@ -2614,7 +2623,8 @@ entry: store ptr %0, ptr %alloca, align 8 %allocaN = alloca i64, align 8 %load = load ptr, ptr %alloca, align 8 - store ptr %load, ptr %allocaN, align 8 + %pti = ptrtoint ptr %load to i64 + store i64 %pti, ptr %allocaN, align 8 %loadN = load i64, ptr %allocaN, align 8 %icmp = icmp eq i64 %loadN, 0 br i1 %icmp, label %if.then.187, label %if.else.188 @@ -2641,7 +2651,8 @@ entry: store ptr %0, ptr %alloca, align 8 %allocaN = alloca i64, align 8 %load = load ptr, ptr %alloca, align 8 - store ptr %load, ptr %allocaN, align 8 + %pti = ptrtoint ptr %load to i64 + store i64 %pti, ptr %allocaN, align 8 %loadN = load i64, ptr %allocaN, align 8 %icmp = icmp eq i64 %loadN, 0 br i1 %icmp, label %if.then.190, label %if.else.191 @@ -2668,7 +2679,8 @@ entry: store ptr %0, ptr %alloca, align 8 %allocaN = alloca i64, align 8 %load = load ptr, ptr %alloca, align 8 - store ptr %load, ptr %allocaN, align 8 + %pti = ptrtoint ptr %load to i64 + store i64 %pti, ptr %allocaN, align 8 %loadN = load i64, ptr %allocaN, align 8 %icmp = icmp eq i64 %loadN, 0 br i1 %icmp, label %if.then.193, label %if.else.194 @@ -2695,7 +2707,8 @@ entry: store ptr %0, ptr %alloca, align 8 %allocaN = alloca i64, align 8 %load = load ptr, ptr %alloca, align 8 - store ptr %load, ptr %allocaN, align 8 + %pti = ptrtoint ptr %load to i64 + store i64 %pti, ptr %allocaN, align 8 %loadN = load i64, ptr %allocaN, align 8 %icmp = icmp eq i64 %loadN, 0 br i1 %icmp, label %if.then.196, label %if.else.197 @@ -2722,7 +2735,8 @@ entry: store ptr %0, ptr %alloca, align 8 %allocaN = alloca i64, align 8 %load = load ptr, ptr %alloca, align 8 - store ptr %load, ptr %allocaN, align 8 + %pti = ptrtoint ptr %load to i64 + store i64 %pti, ptr %allocaN, align 8 %loadN = load i64, ptr %allocaN, align 8 %icmp = icmp eq i64 %loadN, 0 br i1 %icmp, label %if.then.199, label %if.else.200 @@ -2749,7 +2763,8 @@ entry: store ptr %0, ptr %alloca, align 8 %allocaN = alloca i64, align 8 %load = load ptr, ptr %alloca, align 8 - store ptr %load, ptr %allocaN, align 8 + %pti = ptrtoint ptr %load to i64 + store i64 %pti, ptr %allocaN, align 8 %loadN = load i64, ptr %allocaN, align 8 %icmp = icmp eq i64 %loadN, 0 br i1 %icmp, label %if.then.202, label %if.else.203 @@ -2776,7 +2791,8 @@ entry: store ptr %0, ptr %alloca, align 8 %allocaN = alloca i64, align 8 %load = load ptr, ptr %alloca, align 8 - store ptr %load, ptr %allocaN, align 8 + %pti = ptrtoint ptr %load to i64 + store i64 %pti, ptr %allocaN, align 8 %loadN = load i64, ptr %allocaN, align 8 %icmp = icmp eq i64 %loadN, 0 br i1 %icmp, label %if.then.205, label %if.else.206 @@ -2803,7 +2819,8 @@ entry: store ptr %0, ptr %alloca, align 8 %allocaN = alloca i64, align 8 %load = load ptr, ptr %alloca, align 8 - store ptr %load, ptr %allocaN, align 8 + %pti = ptrtoint ptr %load to i64 + store i64 %pti, ptr %allocaN, align 8 %loadN = load i64, ptr %allocaN, align 8 %icmp = icmp eq i64 %loadN, 0 br i1 %icmp, label %if.then.208, label %if.else.209 diff --git a/tests/expected/issue-0037.txt b/tests/expected/issue-0037.txt deleted file mode 100644 index eca5a14..0000000 --- a/tests/expected/issue-0037.txt +++ /dev/null @@ -1,3 +0,0 @@ -direct non-null = true -helper non-null = false -eq = false