From 659cdc227652935ff2211cea64b776fab7185134 Mon Sep 17 00:00:00 2001 From: agra Date: Mon, 25 May 2026 22:08:23 +0300 Subject: [PATCH] ffi M1.2 A.2c + A.3: eager body lowering + self.field via state struct MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds Pass 4b 'lowerObjcDefinedClassMethods' to lowerRoot: after scan, walk objc_defined_class_cache and force-lower each bodied instance method. The Obj-C runtime invokes these via the IMP pointers wired up in A.4 — no sx-side call path drives lazy lowering, so we trigger it here. Mirrors the JNI eager-lower pattern in Pass 5. Bug fix: lazyLowerFunction has its OWN inline body-lowering path (separate from lowerFunction) that re-resolves param types at line 1025. It was running without current_foreign_class set, so '*Self' fell through to the type_bridge fallback and got interned as a 0-field struct named 'Self' — body's 'self.counter' GEP'd into '{}' and LLVM verification rejected. Fix: set current_foreign_class at the top of lazyLowerFunction via the same lookupObjcDefinedClassForMethod path lowerFunction uses. Save+restore via defer. A.3 ('self.field access via the ivar') falls out for free — '*Self' resolves to '*__SxFooState' so 'self.counter' is a plain struct field access. IR snapshot in 142-objc-class-method-lowering.ir shows the round-trip: define internal void @SxFoo.bump(ptr, ptr self) { %gep = getelementptr inbounds { i32 }, ptr %self, 0, 0 %v = load i32, ptr %gep store i32 (%v + 1), ptr %gep ret void } 171 examples pass (+1 from 142); zig build test green. Still gated: Obj-C runtime dispatch (A.7) — sx-side 'f.bump()' calls bail at lower.zig:4407 with the existing diagnostic. IMP-trampoline emission (the C-ABI shim that bridges 'objc_msgSend' → this body) lands in A.4 alongside class-pair init. --- examples/142-objc-class-method-lowering.sx | 42 + src/ir/lower.zig | 39 + .../142-objc-class-method-lowering.exit | 1 + .../142-objc-class-method-lowering.ir | 780 ++++++++++++++++++ .../142-objc-class-method-lowering.txt | 1 + 5 files changed, 863 insertions(+) create mode 100644 examples/142-objc-class-method-lowering.sx create mode 100644 tests/expected/142-objc-class-method-lowering.exit create mode 100644 tests/expected/142-objc-class-method-lowering.ir create mode 100644 tests/expected/142-objc-class-method-lowering.txt diff --git a/examples/142-objc-class-method-lowering.sx b/examples/142-objc-class-method-lowering.sx new file mode 100644 index 0000000..ac4fdbf --- /dev/null +++ b/examples/142-objc-class-method-lowering.sx @@ -0,0 +1,42 @@ +// M1.2 A.2c / A.3 — instance-method body lowering on sx-defined +// `#objc_class`. +// +// The Obj-C runtime invokes class methods via the IMP pointers +// wired up in M1.2 A.4. No sx-side call path drives lazy lowering, +// so the eager pass `lowerObjcDefinedClassMethods` walks the +// `objc_defined_class_cache` and force-lowers every bodied method +// after Pass 1 finishes. +// +// `*Self` substitution (A.2b) routes the param's pointee type to +// the hidden state-struct `__State`. `self.counter` +// then resolves as a plain struct field access — A.3's "free if +// types align". The IR snapshot pins the round-trip: +// +// define internal void @SxFoo.bump(ptr __sx_ctx, ptr self) { +// %gep = getelementptr inbounds { i32 }, ptr %self, 0, 0 +// %v = load i32, ptr %gep +// %inc = add i32 %v, 1 +// store i32 %inc, ptr %gep +// ret void +// } +// +// IMP-trampoline emission (the C-ABI shim that the Obj-C runtime +// calls and that reads the `__sx_state` ivar) lands separately +// in M1.2 A.4 alongside class-pair init. Runtime dispatch +// (M1.2 A.7) stays gated until then. + +#import "modules/std.sx"; +#import "modules/compiler.sx"; + +SxFoo :: #objc_class("SxFoo") { + counter: s32; + + bump :: (self: *Self) { + self.counter += 1; + } +} + +main :: () -> s32 { + print("compiled\n"); + 0; +} diff --git a/src/ir/lower.zig b/src/ir/lower.zig index f56c987..96aca5b 100644 --- a/src/ir/lower.zig +++ b/src/ir/lower.zig @@ -247,6 +247,12 @@ pub const Lowering = struct { self.lowerDeferredTypeFns(); // Pass 4: target-specific entry-point sanity checks self.checkRequiredEntryPoints(); + // Pass 4b: eagerly lower bodied methods on sx-defined `#objc_class` + // declarations. The Obj-C runtime calls these via IMP pointers + // registered in M1.2 A.4 — no sx-side call path drives lazy + // lowering, so we trigger it here. Mirrors the JNI eager-lower + // pattern in Pass 5. + self.lowerObjcDefinedClassMethods(); // Pass 5: synthesize JNI-mangled exports for `#jni_main` bodied methods. // Android's JNI runtime resolves `private native sx_(...)` declared in // the bundled classes.dex by looking up the symbol @@ -886,6 +892,17 @@ pub const Lowering = struct { fn lazyLowerFunction(self: *Lowering, name: []const u8) void { // Already lowered? if (self.lowered_functions.contains(name)) return; + + // For sx-defined `#objc_class` methods, pin current_foreign_class + // so `*Self` substitutions in resolveTypeWithBindings find the + // state-struct type (M1.2 A.2b). The inline body-lowering path + // below re-resolves param types, so the context must be set + // BEFORE any resolveReturnType / resolveParamType call. + const saved_fc_lazy = self.current_foreign_class; + defer self.current_foreign_class = saved_fc_lazy; + if (self.lookupObjcDefinedClassForMethod(name)) |fcd| { + self.current_foreign_class = fcd; + } // No AST? (builtins, foreign functions, or imported functions not in this file) const fd = self.fn_ast_map.get(name) orelse return; // Foreign declarations stay as extern stubs but need to be REGISTERED @@ -11399,6 +11416,28 @@ pub const Lowering = struct { /// receiver), followed by the user-declared params with pointer types /// type-erased to `*void` (JNI carries jobjects, not sx-typed handles — /// future work can keep richer typing inside the body when needed). + /// Eagerly lower bodied instance methods on every sx-defined + /// `#objc_class`. The Obj-C runtime invokes these via the IMP + /// pointers wired up in M1.2 A.4 — no sx-side call path triggers + /// lazy lowering, so we walk the cache and force-lower here. + /// `lowerFunction` sets `current_foreign_class` automatically based + /// on the qualified name, so `*Self` substitutions in the body + /// resolve correctly (M1.2 A.2b). + fn lowerObjcDefinedClassMethods(self: *Lowering) void { + for (self.module.objc_defined_class_cache.items) |entry| { + const fcd = entry.decl; + for (fcd.members) |m| { + const method = switch (m) { + .method => |md| md, + else => continue, + }; + if (method.body == null) continue; + const qualified = std.fmt.allocPrint(self.alloc, "{s}.{s}", .{ fcd.name, method.name }) catch continue; + self.lazyLowerFunction(qualified); + } + } + } + fn synthesizeJniMainStubs(self: *Lowering) void { var seen = std.StringHashMap(void).init(self.alloc); defer seen.deinit(); diff --git a/tests/expected/142-objc-class-method-lowering.exit b/tests/expected/142-objc-class-method-lowering.exit new file mode 100644 index 0000000..573541a --- /dev/null +++ b/tests/expected/142-objc-class-method-lowering.exit @@ -0,0 +1 @@ +0 diff --git a/tests/expected/142-objc-class-method-lowering.ir b/tests/expected/142-objc-class-method-lowering.ir new file mode 100644 index 0000000..ee06285 --- /dev/null +++ b/tests/expected/142-objc-class-method-lowering.ir @@ -0,0 +1,780 @@ + +@OS = internal global i64 0 +@ARCH = internal global i64 0 +@POINTER_SIZE = internal global i64 8 +@__sx_default_context = internal global { { ptr, ptr, ptr }, ptr } { { ptr, ptr, ptr } { ptr null, ptr @__thunk_CAllocator_Allocator_alloc, ptr @__thunk_CAllocator_Allocator_dealloc }, ptr null } +@str = private unnamed_addr constant [2 x i8] c"0\00", align 1 +@str.1 = private unnamed_addr constant [15 x i8] c"result := \22\22; \00", align 1 +@str.2 = private unnamed_addr constant [37 x i8] c"result = concat(result, substr(fmt, \00", align 1 +@str.3 = private unnamed_addr constant [3 x i8] c", \00", align 1 +@str.4 = private unnamed_addr constant [5 x i8] c")); \00", align 1 +@str.5 = private unnamed_addr constant [44 x i8] c"result = concat(result, any_to_string(args[\00", align 1 +@str.6 = private unnamed_addr constant [6 x i8] c"])); \00", align 1 +@str.7 = private unnamed_addr constant [37 x i8] c"result = concat(result, substr(fmt, \00", align 1 +@str.8 = private unnamed_addr constant [3 x i8] c", \00", align 1 +@str.9 = private unnamed_addr constant [5 x i8] c")); \00", align 1 +@str.10 = private unnamed_addr constant [37 x i8] c"result = concat(result, substr(fmt, \00", align 1 +@str.11 = private unnamed_addr constant [3 x i8] c", \00", align 1 +@str.12 = private unnamed_addr constant [5 x i8] c")); \00", align 1 +@str.13 = private unnamed_addr constant [37 x i8] c"result = concat(result, substr(fmt, \00", align 1 +@str.14 = private unnamed_addr constant [3 x i8] c", \00", align 1 +@str.15 = private unnamed_addr constant [5 x i8] c")); \00", align 1 +@str.16 = private unnamed_addr constant [10 x i8] c"compiled\0A\00", align 1 +@str.17 = private unnamed_addr constant [1 x i8] zeroinitializer, align 1 +@str.18 = private unnamed_addr constant [10 x i8] c"compiled\0A\00", align 1 + +; Function Attrs: nounwind +declare void @out(ptr) #0 + +declare ptr @malloc(i64) + +declare void @free(ptr) + +declare ptr @memcpy(ptr, ptr, i64) + +declare ptr @memset(ptr, i32, i64) + +; Function Attrs: nounwind +define internal ptr @CAllocator.alloc(ptr %0, ptr %1, i64 %2) #0 { +entry: + %alloca = alloca ptr, align 8 + store ptr %1, ptr %alloca, align 8 + %allocaN = alloca i64, align 8 + store i64 %2, ptr %allocaN, align 8 + %load = load i64, ptr %allocaN, align 8 + %call = call ptr @malloc(i64 %load) + ret ptr %call +} + +; Function Attrs: nounwind +define internal void @CAllocator.dealloc(ptr %0, ptr %1, ptr %2) #0 { +entry: + %alloca = alloca ptr, align 8 + store ptr %1, ptr %alloca, align 8 + %allocaN = alloca ptr, align 8 + store ptr %2, ptr %allocaN, align 8 + %load = load ptr, ptr %allocaN, align 8 + call void @free(ptr %load) + ret void +} + +; Function Attrs: nounwind +declare i64 @GPA.init(ptr) #0 + +; Function Attrs: nounwind +declare ptr @GPA.alloc(ptr, ptr, i64) #0 + +; Function Attrs: nounwind +declare void @GPA.dealloc(ptr, ptr, ptr) #0 + +; Function Attrs: nounwind +declare void @Arena.add_chunk(ptr, ptr, i64) #0 + +; Function Attrs: nounwind +declare void @Arena.init(ptr sret({ ptr, i64, { ptr, ptr, ptr } }), ptr, ptr, i64) #0 + +; Function Attrs: nounwind +declare void @Arena.reset(ptr, ptr) #0 + +; Function Attrs: nounwind +declare void @Arena.deinit(ptr, ptr) #0 + +; Function Attrs: nounwind +declare ptr @Arena.alloc(ptr, ptr, i64) #0 + +; Function Attrs: nounwind +declare void @Arena.dealloc(ptr, ptr, ptr) #0 + +; Function Attrs: nounwind +declare ptr @BufAlloc.init(ptr, ptr, i64) #0 + +; Function Attrs: nounwind +declare void @BufAlloc.reset(ptr, ptr) #0 + +; Function Attrs: nounwind +declare ptr @BufAlloc.alloc(ptr, ptr, i64) #0 + +; Function Attrs: nounwind +declare void @BufAlloc.dealloc(ptr, ptr, ptr) #0 + +; Function Attrs: nounwind +declare void @TrackingAllocator.init(ptr sret({ { ptr, ptr, ptr }, i64, i64, i64 }), ptr, ptr) #0 + +; Function Attrs: nounwind +declare i64 @TrackingAllocator.leak_count(ptr, ptr) #0 + +; Function Attrs: nounwind +declare void @TrackingAllocator.report(ptr, ptr) #0 + +; Function Attrs: nounwind +declare ptr @TrackingAllocator.alloc(ptr, ptr, i64) #0 + +; Function Attrs: nounwind +declare void @TrackingAllocator.dealloc(ptr, ptr, ptr) #0 + +; Function Attrs: nounwind +define internal { ptr, i64 } @cstring(ptr %0, i64 %1) #0 { +entry: + %alloca = alloca i64, align 8 + store i64 %1, ptr %alloca, align 8 + %load = load i64, ptr %alloca, align 8 + %add = add i64 %load, 1 + %loadN = load { { ptr, ptr, ptr }, ptr }, ptr %0, align 8 + %sg = extractvalue { { ptr, ptr, ptr }, ptr } %loadN, 0 + %sgN = extractvalue { ptr, ptr, ptr } %sg, 0 + %sgN = extractvalue { ptr, ptr, ptr } %sg, 1 + %icall = call ptr %sgN(ptr %0, ptr %sgN, i64 %add) + %allocaN = alloca ptr, align 8 + store ptr %icall, ptr %allocaN, align 8 + %loadN = load ptr, ptr %allocaN, align 8 + %loadN = load i64, ptr %alloca, align 8 + %addN = add i64 %loadN, 1 + %2 = call ptr @memset(ptr %loadN, i32 0, i64 %addN) + %allocaN = alloca { ptr, i64 }, align 8 + 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 + %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 + %loadN = load { ptr, i64 }, ptr %allocaN, align 8 + ret { ptr, i64 } %loadN +} + +; Function Attrs: nounwind +define internal { ptr, i64 } @int_to_string(ptr %0, i64 %1) #0 { +entry: + %alloca = alloca i64, align 8 + store i64 %1, ptr %alloca, align 8 + %load = load i64, ptr %alloca, align 8 + %icmp = icmp eq i64 %load, 0 + br i1 %icmp, label %if.then.14, label %if.merge.15 + +if.then.14: ; preds = %entry + ret { ptr, i64 } { ptr @str, i64 1 } + +if.merge.15: ; preds = %entry + %loadN = load i64, ptr %alloca, align 8 + %icmpN = icmp slt i64 %loadN, 0 + %allocaN = alloca i1, align 1 + store i1 %icmpN, ptr %allocaN, align 1 + %loadN = load i1, ptr %allocaN, align 1 + br i1 %loadN, label %if.then.16, label %if.else.17 + +if.then.16: ; preds = %if.merge.15 + %loadN = load i64, ptr %alloca, align 8 + %sub = sub i64 0, %loadN + br label %if.merge.18 + +if.else.17: ; preds = %if.merge.15 + %loadN = load i64, ptr %alloca, align 8 + br label %if.merge.18 + +if.merge.18: ; preds = %if.else.17, %if.then.16 + %bp = phi i64 [ %sub, %if.then.16 ], [ %loadN, %if.else.17 ] + %allocaN = alloca i64, align 8 + store i64 %bp, ptr %allocaN, align 8 + %call = call { ptr, i64 } @cstring(ptr %0, i64 20) + %allocaN = alloca { ptr, i64 }, align 8 + store { ptr, i64 } %call, ptr %allocaN, align 8 + %allocaN = alloca i64, align 8 + store i64 19, ptr %allocaN, align 8 + br label %while.hdr.19 + +while.hdr.19: ; preds = %while.body.20, %if.merge.18 + %loadN = load i64, ptr %allocaN, align 8 + %icmpN = icmp sgt i64 %loadN, 0 + br i1 %icmpN, label %while.body.20, label %while.exit.21 + +while.body.20: ; preds = %while.hdr.19 + %loadN = load i64, ptr %allocaN, align 8 + %srem = srem i64 %loadN, 10 + %add = add i64 %srem, 48 + %loadN = load i64, ptr %allocaN, align 8 + %loadN = load { ptr, i64 }, ptr %allocaN, align 8 + %igp.data = extractvalue { ptr, i64 } %loadN, 0 + %igp.ptr = getelementptr i8, ptr %igp.data, i64 %loadN + %trunc = trunc i64 %add to i8 + store i8 %trunc, ptr %igp.ptr, align 1 + %loadN = load i64, ptr %allocaN, align 8 + %sdiv = sdiv i64 %loadN, 10 + store i64 %sdiv, ptr %allocaN, align 8 + %loadN = load i64, ptr %allocaN, align 8 + %subN = sub i64 %loadN, 1 + store i64 %subN, ptr %allocaN, align 8 + br label %while.hdr.19 + +while.exit.21: ; preds = %while.hdr.19 + %loadN = load i1, ptr %allocaN, align 1 + br i1 %loadN, label %if.then.22, label %if.merge.23 + +if.then.22: ; preds = %while.exit.21 + %loadN = load i64, ptr %allocaN, align 8 + %loadN = load { ptr, i64 }, ptr %allocaN, align 8 + %igp.data21 = extractvalue { ptr, i64 } %loadN, 0 + %igp.ptr22 = getelementptr i8, ptr %igp.data21, i64 %loadN + store i8 45, ptr %igp.ptr22, align 1 + %loadN = load i64, ptr %allocaN, align 8 + %subN = sub i64 %loadN, 1 + store i64 %subN, ptr %allocaN, align 8 + br label %if.merge.23 + +if.merge.23: ; preds = %if.then.22, %while.exit.21 + %loadN = load { ptr, i64 }, ptr %allocaN, align 8 + %loadN = load i64, ptr %allocaN, align 8 + %addN = add i64 %loadN, 1 + %loadN = load i64, ptr %allocaN, align 8 + %subN = sub i64 20, %loadN + %subN = sub i64 %subN, 1 + %callN = call { ptr, i64 } @substr(ptr %0, { ptr, i64 } %loadN, i64 %addN, i64 %subN) + ret { ptr, i64 } %callN +} + +; Function Attrs: nounwind +declare ptr @bool_to_string(ptr, i1) #0 + +; Function Attrs: nounwind +declare ptr @float_to_string(ptr, double) #0 + +; Function Attrs: nounwind +declare void @hex_group(ptr, ptr, i64, i64) #0 + +; Function Attrs: nounwind +declare ptr @int_to_hex_string(ptr, i64) #0 + +; Function Attrs: nounwind +define internal { ptr, i64 } @concat(ptr %0, { ptr, i64 } %1, { ptr, i64 } %2) #0 { +entry: + %alloca = alloca { ptr, i64 }, align 8 + store { ptr, i64 } %1, ptr %alloca, align 8 + %allocaN = alloca { ptr, i64 }, align 8 + store { ptr, i64 } %2, ptr %allocaN, align 8 + %load = load { ptr, i64 }, ptr %alloca, align 8 + %len = extractvalue { ptr, i64 } %load, 1 + %allocaN = alloca i64, align 8 + store i64 %len, ptr %allocaN, align 8 + %loadN = load { ptr, i64 }, ptr %allocaN, align 8 + %lenN = extractvalue { ptr, i64 } %loadN, 1 + %allocaN = alloca i64, align 8 + store i64 %lenN, ptr %allocaN, align 8 + %loadN = load i64, ptr %allocaN, align 8 + %loadN = load i64, ptr %allocaN, align 8 + %add = add i64 %loadN, %loadN + %call = call { ptr, i64 } @cstring(ptr %0, i64 %add) + %allocaN = alloca { ptr, i64 }, align 8 + store { ptr, i64 } %call, ptr %allocaN, align 8 + %loadN = load { ptr, i64 }, ptr %allocaN, align 8 + %dptr = extractvalue { ptr, i64 } %loadN, 0 + %loadN = load { ptr, i64 }, ptr %alloca, align 8 + %dptrN = extractvalue { ptr, i64 } %loadN, 0 + %loadN = load i64, ptr %allocaN, align 8 + %callN = call ptr @memcpy(ptr %dptr, ptr %dptrN, i64 %loadN) + %loadN = load i64, ptr %allocaN, align 8 + %loadN = load { ptr, i64 }, ptr %allocaN, align 8 + %igp.data = extractvalue { ptr, i64 } %loadN, 0 + %igp.ptr = getelementptr i8, ptr %igp.data, i64 %loadN + %loadN = load { ptr, i64 }, ptr %allocaN, align 8 + %dptrN = extractvalue { ptr, i64 } %loadN, 0 + %loadN = load i64, ptr %allocaN, align 8 + %callN = call ptr @memcpy(ptr %igp.ptr, ptr %dptrN, i64 %loadN) + %loadN = load { ptr, i64 }, ptr %allocaN, align 8 + ret { ptr, i64 } %loadN +} + +; Function Attrs: nounwind +define internal { ptr, i64 } @substr(ptr %0, { ptr, i64 } %1, i64 %2, i64 %3) #0 { +entry: + %alloca = alloca { ptr, i64 }, align 8 + store { ptr, i64 } %1, ptr %alloca, align 8 + %allocaN = alloca i64, align 8 + store i64 %2, ptr %allocaN, align 8 + %allocaN = alloca i64, align 8 + store i64 %3, ptr %allocaN, align 8 + %load = load i64, ptr %allocaN, align 8 + %call = call { ptr, i64 } @cstring(ptr %0, i64 %load) + %allocaN = alloca { ptr, i64 }, align 8 + store { ptr, i64 } %call, ptr %allocaN, align 8 + %loadN = load { ptr, i64 }, ptr %allocaN, align 8 + %dptr = extractvalue { ptr, i64 } %loadN, 0 + %loadN = load i64, ptr %allocaN, align 8 + %loadN = load { ptr, i64 }, ptr %alloca, align 8 + %igp.data = extractvalue { ptr, i64 } %loadN, 0 + %igp.ptr = getelementptr i8, ptr %igp.data, i64 %loadN + %loadN = load i64, ptr %allocaN, align 8 + %callN = call ptr @memcpy(ptr %dptr, ptr %igp.ptr, i64 %loadN) + %loadN = load { ptr, i64 }, ptr %allocaN, align 8 + ret { ptr, i64 } %loadN +} + +; Function Attrs: nounwind +declare ptr @xml_escape(ptr, ptr) #0 + +; Function Attrs: nounwind +declare ptr @path_join(ptr, ptr) #0 + +; Function Attrs: nounwind +declare ptr @any_to_string(ptr, [2 x i64]) #0 + +; Function Attrs: nounwind +define internal { ptr, i64 } @build_format(ptr %0, { ptr, i64 } %1) #0 { +entry: + %alloca = alloca { ptr, i64 }, align 8 + store { ptr, i64 } %1, ptr %alloca, align 8 + %allocaN = alloca { ptr, i64 }, align 8 + store { ptr, i64 } { ptr @str.1, i64 14 }, ptr %allocaN, align 8 + %allocaN = alloca i64, align 8 + store i64 0, ptr %allocaN, align 8 + %allocaN = alloca i64, align 8 + store i64 0, ptr %allocaN, align 8 + %allocaN = alloca i64, align 8 + store i64 0, ptr %allocaN, align 8 + br label %while.hdr.0 + +while.hdr.0: ; preds = %if.merge.5, %entry + %load = load i64, ptr %allocaN, align 8 + %loadN = load { ptr, i64 }, ptr %alloca, align 8 + %len = extractvalue { ptr, i64 } %loadN, 1 + %icmp = icmp slt i64 %load, %len + br i1 %icmp, label %while.body.1, label %while.exit.2 + +while.body.1: ; preds = %while.hdr.0 + %loadN = load { ptr, i64 }, ptr %alloca, align 8 + %loadN = load i64, ptr %allocaN, align 8 + %ig.data = extractvalue { ptr, i64 } %loadN, 0 + %ig.ptr = getelementptr i8, ptr %ig.data, i64 %loadN + %ig.val = load i8, ptr %ig.ptr, align 1 + %cmp.ext = zext i8 %ig.val to i64 + %icmpN = icmp eq i64 %cmp.ext, 123 + br i1 %icmpN, label %if.then.3, label %if.else.4 + +while.exit.2: ; preds = %while.hdr.0 + %loadN = load i64, ptr %allocaN, align 8 + %loadN = load { ptr, i64 }, ptr %alloca, align 8 + %lenN = extractvalue { ptr, i64 } %loadN, 1 + %icmpN = icmp slt i64 %loadN, %lenN + br i1 %icmpN, label %if.then.36, label %if.merge.37 + +if.then.3: ; preds = %while.body.1 + %loadN = load i64, ptr %allocaN, align 8 + %add = add i64 %loadN, 1 + %loadN = load { ptr, i64 }, ptr %alloca, align 8 + %lenN = extractvalue { ptr, i64 } %loadN, 1 + %icmpN = icmp slt i64 %add, %lenN + br i1 %icmpN, label %if.then.6, label %if.else.7 + +if.else.4: ; preds = %while.body.1 + %loadN = load { ptr, i64 }, ptr %alloca, align 8 + %loadN = load i64, ptr %allocaN, align 8 + %ig.data19 = extractvalue { ptr, i64 } %loadN, 0 + %ig.ptr20 = getelementptr i8, ptr %ig.data19, i64 %loadN + %ig.val21 = load i8, ptr %ig.ptr20, align 1 + %cmp.ext22 = zext i8 %ig.val21 to i64 + %icmpN = icmp eq i64 %cmp.ext22, 125 + br i1 %icmpN, label %if.then.27, label %if.else.28 + +if.merge.5: ; preds = %if.merge.29, %if.merge.8 + br label %while.hdr.0 + +if.then.6: ; preds = %if.then.3 + %loadN = load { ptr, i64 }, ptr %alloca, align 8 + %loadN = load i64, ptr %allocaN, align 8 + %addN = add i64 %loadN, 1 + %ig.data27 = extractvalue { ptr, i64 } %loadN, 0 + %ig.ptr28 = getelementptr i8, ptr %ig.data27, i64 %addN + %ig.val29 = load i8, ptr %ig.ptr28, align 1 + %cmp.ext30 = zext i8 %ig.val29 to i64 + %icmpN = icmp eq i64 %cmp.ext30, 125 + br i1 %icmpN, label %if.then.9, label %if.else.10 + +if.else.7: ; preds = %if.then.3 + %loadN = load i64, ptr %allocaN, align 8 + %addN = add i64 %loadN, 1 + store i64 %addN, ptr %allocaN, align 8 + br label %if.merge.8 + +if.merge.8: ; preds = %if.merge.11, %if.else.7 + br label %if.merge.5 + +if.then.9: ; preds = %if.then.6 + %loadN = load i64, ptr %allocaN, align 8 + %loadN = load i64, ptr %allocaN, align 8 + %icmpN = icmp sgt i64 %loadN, %loadN + br i1 %icmpN, label %if.then.12, label %if.merge.13 + +if.else.10: ; preds = %if.then.6 + %loadN = load { ptr, i64 }, ptr %alloca, align 8 + %loadN = load i64, ptr %allocaN, align 8 + %addN = add i64 %loadN, 1 + %ig.data40 = extractvalue { ptr, i64 } %loadN, 0 + %ig.ptr41 = getelementptr i8, ptr %ig.data40, i64 %addN + %ig.val42 = load i8, ptr %ig.ptr41, align 1 + %cmp.ext43 = zext i8 %ig.val42 to i64 + %icmpN = icmp eq i64 %cmp.ext43, 123 + br i1 %icmpN, label %if.then.24, label %if.else.25 + +if.merge.11: ; preds = %if.merge.26, %if.merge.13 + br label %if.merge.8 + +if.then.12: ; preds = %if.then.9 + %loadN = load { ptr, i64 }, ptr %allocaN, align 8 + %call = call { ptr, i64 } @concat(ptr %0, { ptr, i64 } %loadN, { ptr, i64 } { ptr @str.2, i64 36 }) + store { ptr, i64 } %call, ptr %allocaN, align 8 + %loadN = load { ptr, i64 }, ptr %allocaN, align 8 + %loadN = load i64, ptr %allocaN, align 8 + %callN = call { ptr, i64 } @int_to_string(ptr %0, i64 %loadN) + %callN = call { ptr, i64 } @concat(ptr %0, { ptr, i64 } %loadN, { ptr, i64 } %callN) + store { ptr, i64 } %callN, ptr %allocaN, align 8 + %loadN = load { ptr, i64 }, ptr %allocaN, align 8 + %callN = call { ptr, i64 } @concat(ptr %0, { ptr, i64 } %loadN, { ptr, i64 } { ptr @str.3, i64 2 }) + store { ptr, i64 } %callN, ptr %allocaN, align 8 + %loadN = load { ptr, i64 }, ptr %allocaN, align 8 + %loadN = load i64, ptr %allocaN, align 8 + %loadN = load i64, ptr %allocaN, align 8 + %sub = sub i64 %loadN, %loadN + %callN = call { ptr, i64 } @int_to_string(ptr %0, i64 %sub) + %callN = call { ptr, i64 } @concat(ptr %0, { ptr, i64 } %loadN, { ptr, i64 } %callN) + store { ptr, i64 } %callN, ptr %allocaN, align 8 + %loadN = load { ptr, i64 }, ptr %allocaN, align 8 + %callN = call { ptr, i64 } @concat(ptr %0, { ptr, i64 } %loadN, { ptr, i64 } { ptr @str.4, i64 4 }) + store { ptr, i64 } %callN, ptr %allocaN, align 8 + br label %if.merge.13 + +if.merge.13: ; preds = %if.then.12, %if.then.9 + %loadN = load { ptr, i64 }, ptr %allocaN, align 8 + %callN = call { ptr, i64 } @concat(ptr %0, { ptr, i64 } %loadN, { ptr, i64 } { ptr @str.5, i64 43 }) + store { ptr, i64 } %callN, ptr %allocaN, align 8 + %loadN = load { ptr, i64 }, ptr %allocaN, align 8 + %loadN = load i64, ptr %allocaN, align 8 + %callN = call { ptr, i64 } @int_to_string(ptr %0, i64 %loadN) + %callN = call { ptr, i64 } @concat(ptr %0, { ptr, i64 } %loadN, { ptr, i64 } %callN) + store { ptr, i64 } %callN, ptr %allocaN, align 8 + %loadN = load { ptr, i64 }, ptr %allocaN, align 8 + %callN = call { ptr, i64 } @concat(ptr %0, { ptr, i64 } %loadN, { ptr, i64 } { ptr @str.6, i64 5 }) + store { ptr, i64 } %callN, ptr %allocaN, align 8 + %loadN = load i64, ptr %allocaN, align 8 + %addN = add i64 %loadN, 1 + store i64 %addN, ptr %allocaN, align 8 + %loadN = load i64, ptr %allocaN, align 8 + %addN = add i64 %loadN, 2 + store i64 %addN, ptr %allocaN, align 8 + %loadN = load i64, ptr %allocaN, align 8 + store i64 %loadN, ptr %allocaN, align 8 + br label %if.merge.11 + +if.then.24: ; preds = %if.else.10 + %loadN = load { ptr, i64 }, ptr %allocaN, align 8 + %callN = call { ptr, i64 } @concat(ptr %0, { ptr, i64 } %loadN, { ptr, i64 } { ptr @str.7, i64 36 }) + store { ptr, i64 } %callN, ptr %allocaN, align 8 + %loadN = load { ptr, i64 }, ptr %allocaN, align 8 + %loadN = load i64, ptr %allocaN, align 8 + %callN = call { ptr, i64 } @int_to_string(ptr %0, i64 %loadN) + %callN = call { ptr, i64 } @concat(ptr %0, { ptr, i64 } %loadN, { ptr, i64 } %callN) + store { ptr, i64 } %callN, ptr %allocaN, align 8 + %loadN = load { ptr, i64 }, ptr %allocaN, align 8 + %callN = call { ptr, i64 } @concat(ptr %0, { ptr, i64 } %loadN, { ptr, i64 } { ptr @str.8, i64 2 }) + store { ptr, i64 } %callN, ptr %allocaN, align 8 + %loadN = load { ptr, i64 }, ptr %allocaN, align 8 + %loadN = load i64, ptr %allocaN, align 8 + %loadN = load i64, ptr %allocaN, align 8 + %subN = sub i64 %loadN, %loadN + %addN = add i64 %subN, 1 + %callN = call { ptr, i64 } @int_to_string(ptr %0, i64 %addN) + %callN = call { ptr, i64 } @concat(ptr %0, { ptr, i64 } %loadN, { ptr, i64 } %callN) + store { ptr, i64 } %callN, ptr %allocaN, align 8 + %loadN = load { ptr, i64 }, ptr %allocaN, align 8 + %callN = call { ptr, i64 } @concat(ptr %0, { ptr, i64 } %loadN, { ptr, i64 } { ptr @str.9, i64 4 }) + store { ptr, i64 } %callN, ptr %allocaN, align 8 + %loadN = load i64, ptr %allocaN, align 8 + %addN = add i64 %loadN, 2 + store i64 %addN, ptr %allocaN, align 8 + %loadN = load i64, ptr %allocaN, align 8 + store i64 %loadN, ptr %allocaN, align 8 + br label %if.merge.26 + +if.else.25: ; preds = %if.else.10 + %loadN = load i64, ptr %allocaN, align 8 + %addN = add i64 %loadN, 1 + store i64 %addN, ptr %allocaN, align 8 + br label %if.merge.26 + +if.merge.26: ; preds = %if.else.25, %if.then.24 + br label %if.merge.11 + +if.then.27: ; preds = %if.else.4 + %loadN = load i64, ptr %allocaN, align 8 + %addN = add i64 %loadN, 1 + %loadN = load { ptr, i64 }, ptr %alloca, align 8 + %lenN = extractvalue { ptr, i64 } %loadN, 1 + %icmpN = icmp slt i64 %addN, %lenN + br i1 %icmpN, label %if.then.30, label %if.else.31 + +if.else.28: ; preds = %if.else.4 + %loadN = load i64, ptr %allocaN, align 8 + %addN = add i64 %loadN, 1 + store i64 %addN, ptr %allocaN, align 8 + br label %if.merge.29 + +if.merge.29: ; preds = %if.merge.32, %if.else.28 + br label %if.merge.5 + +if.then.30: ; preds = %if.then.27 + %loadN = load { ptr, i64 }, ptr %alloca, align 8 + %loadN = load i64, ptr %allocaN, align 8 + %addN = add i64 %loadN, 1 + %ig.data104 = extractvalue { ptr, i64 } %loadN, 0 + %ig.ptr105 = getelementptr i8, ptr %ig.data104, i64 %addN + %ig.val106 = load i8, ptr %ig.ptr105, align 1 + %cmp.ext107 = zext i8 %ig.val106 to i64 + %icmpN = icmp eq i64 %cmp.ext107, 125 + br i1 %icmpN, label %if.then.33, label %if.else.34 + +if.else.31: ; preds = %if.then.27 + %loadN = load i64, ptr %allocaN, align 8 + %addN = add i64 %loadN, 1 + store i64 %addN, ptr %allocaN, align 8 + br label %if.merge.32 + +if.merge.32: ; preds = %if.merge.35, %if.else.31 + br label %if.merge.29 + +if.then.33: ; preds = %if.then.30 + %loadN = load { ptr, i64 }, ptr %allocaN, align 8 + %callN = call { ptr, i64 } @concat(ptr %0, { ptr, i64 } %loadN, { ptr, i64 } { ptr @str.10, i64 36 }) + store { ptr, i64 } %callN, ptr %allocaN, align 8 + %loadN = load { ptr, i64 }, ptr %allocaN, align 8 + %loadN = load i64, ptr %allocaN, align 8 + %callN = call { ptr, i64 } @int_to_string(ptr %0, i64 %loadN) + %callN = call { ptr, i64 } @concat(ptr %0, { ptr, i64 } %loadN, { ptr, i64 } %callN) + store { ptr, i64 } %callN, ptr %allocaN, align 8 + %loadN = load { ptr, i64 }, ptr %allocaN, align 8 + %callN = call { ptr, i64 } @concat(ptr %0, { ptr, i64 } %loadN, { ptr, i64 } { ptr @str.11, i64 2 }) + store { ptr, i64 } %callN, ptr %allocaN, align 8 + %loadN = load { ptr, i64 }, ptr %allocaN, align 8 + %loadN = load i64, ptr %allocaN, align 8 + %loadN = load i64, ptr %allocaN, align 8 + %subN = sub i64 %loadN, %loadN + %addN = add i64 %subN, 1 + %callN = call { ptr, i64 } @int_to_string(ptr %0, i64 %addN) + %callN = call { ptr, i64 } @concat(ptr %0, { ptr, i64 } %loadN, { ptr, i64 } %callN) + store { ptr, i64 } %callN, ptr %allocaN, align 8 + %loadN = load { ptr, i64 }, ptr %allocaN, align 8 + %callN = call { ptr, i64 } @concat(ptr %0, { ptr, i64 } %loadN, { ptr, i64 } { ptr @str.12, i64 4 }) + store { ptr, i64 } %callN, ptr %allocaN, align 8 + %loadN = load i64, ptr %allocaN, align 8 + %addN = add i64 %loadN, 2 + store i64 %addN, ptr %allocaN, align 8 + %loadN = load i64, ptr %allocaN, align 8 + store i64 %loadN, ptr %allocaN, align 8 + br label %if.merge.35 + +if.else.34: ; preds = %if.then.30 + %loadN = load i64, ptr %allocaN, align 8 + %addN = add i64 %loadN, 1 + store i64 %addN, ptr %allocaN, align 8 + br label %if.merge.35 + +if.merge.35: ; preds = %if.else.34, %if.then.33 + br label %if.merge.32 + +if.then.36: ; preds = %while.exit.2 + %loadN = load { ptr, i64 }, ptr %allocaN, align 8 + %callN = call { ptr, i64 } @concat(ptr %0, { ptr, i64 } %loadN, { ptr, i64 } { ptr @str.13, i64 36 }) + store { ptr, i64 } %callN, ptr %allocaN, align 8 + %loadN = load { ptr, i64 }, ptr %allocaN, align 8 + %loadN = load i64, ptr %allocaN, align 8 + %callN = call { ptr, i64 } @int_to_string(ptr %0, i64 %loadN) + %callN = call { ptr, i64 } @concat(ptr %0, { ptr, i64 } %loadN, { ptr, i64 } %callN) + store { ptr, i64 } %callN, ptr %allocaN, align 8 + %loadN = load { ptr, i64 }, ptr %allocaN, align 8 + %callN = call { ptr, i64 } @concat(ptr %0, { ptr, i64 } %loadN, { ptr, i64 } { ptr @str.14, i64 2 }) + store { ptr, i64 } %callN, ptr %allocaN, align 8 + %loadN = load { ptr, i64 }, ptr %allocaN, align 8 + %loadN = load { ptr, i64 }, ptr %alloca, align 8 + %lenN = extractvalue { ptr, i64 } %loadN, 1 + %loadN = load i64, ptr %allocaN, align 8 + %subN = sub i64 %lenN, %loadN + %callN = call { ptr, i64 } @int_to_string(ptr %0, i64 %subN) + %callN = call { ptr, i64 } @concat(ptr %0, { ptr, i64 } %loadN, { ptr, i64 } %callN) + store { ptr, i64 } %callN, ptr %allocaN, align 8 + %loadN = load { ptr, i64 }, ptr %allocaN, align 8 + %callN = call { ptr, i64 } @concat(ptr %0, { ptr, i64 } %loadN, { ptr, i64 } { ptr @str.15, i64 4 }) + store { ptr, i64 } %callN, ptr %allocaN, align 8 + br label %if.merge.37 + +if.merge.37: ; preds = %if.then.36, %while.exit.2 + %loadN = load { ptr, i64 }, ptr %allocaN, align 8 + ret { ptr, i64 } %loadN +} + +; Function Attrs: nounwind +declare void @BuildOptions.add_link_flag(i64, ptr) #0 + +; Function Attrs: nounwind +declare void @BuildOptions.add_framework(i64, ptr) #0 + +; Function Attrs: nounwind +declare void @BuildOptions.set_output_path(i64, ptr) #0 + +; Function Attrs: nounwind +declare void @BuildOptions.set_wasm_shell(i64, ptr) #0 + +; Function Attrs: nounwind +declare void @BuildOptions.add_asset_dir(i64, ptr, ptr) #0 + +; Function Attrs: nounwind +declare i64 @BuildOptions.asset_dir_count(i64) #0 + +; Function Attrs: nounwind +declare ptr @BuildOptions.asset_dir_src_at(i64, i64) #0 + +; Function Attrs: nounwind +declare ptr @BuildOptions.asset_dir_dest_at(i64, i64) #0 + +; Function Attrs: nounwind +declare void @BuildOptions.set_post_link_callback(i64, ptr) #0 + +; Function Attrs: nounwind +declare void @BuildOptions.set_post_link_module(i64, ptr) #0 + +; Function Attrs: nounwind +declare ptr @BuildOptions.binary_path(i64) #0 + +; Function Attrs: nounwind +declare void @BuildOptions.set_bundle_path(i64, ptr) #0 + +; Function Attrs: nounwind +declare void @BuildOptions.set_bundle_id(i64, ptr) #0 + +; Function Attrs: nounwind +declare void @BuildOptions.set_codesign_identity(i64, ptr) #0 + +; Function Attrs: nounwind +declare void @BuildOptions.set_provisioning_profile(i64, ptr) #0 + +; Function Attrs: nounwind +declare ptr @BuildOptions.bundle_path(i64) #0 + +; Function Attrs: nounwind +declare ptr @BuildOptions.bundle_id(i64) #0 + +; Function Attrs: nounwind +declare ptr @BuildOptions.codesign_identity(i64) #0 + +; Function Attrs: nounwind +declare ptr @BuildOptions.provisioning_profile(i64) #0 + +; Function Attrs: nounwind +declare ptr @BuildOptions.target_triple(i64) #0 + +; Function Attrs: nounwind +declare i1 @BuildOptions.is_macos(i64) #0 + +; Function Attrs: nounwind +declare i1 @BuildOptions.is_ios(i64) #0 + +; Function Attrs: nounwind +declare i1 @BuildOptions.is_ios_device(i64) #0 + +; Function Attrs: nounwind +declare i1 @BuildOptions.is_ios_simulator(i64) #0 + +; Function Attrs: nounwind +declare i1 @BuildOptions.is_android(i64) #0 + +; Function Attrs: nounwind +declare i64 @BuildOptions.framework_count(i64) #0 + +; Function Attrs: nounwind +declare ptr @BuildOptions.framework_at(i64, i64) #0 + +; Function Attrs: nounwind +declare i64 @BuildOptions.framework_path_count(i64) #0 + +; Function Attrs: nounwind +declare ptr @BuildOptions.framework_path_at(i64, i64) #0 + +; Function Attrs: nounwind +declare void @BuildOptions.set_manifest_path(i64, ptr) #0 + +; Function Attrs: nounwind +declare void @BuildOptions.set_keystore_path(i64, ptr) #0 + +; Function Attrs: nounwind +declare ptr @BuildOptions.manifest_path(i64) #0 + +; Function Attrs: nounwind +declare ptr @BuildOptions.keystore_path(i64) #0 + +; Function Attrs: nounwind +declare i64 @BuildOptions.jni_main_count(i64) #0 + +; Function Attrs: nounwind +declare ptr @BuildOptions.jni_main_foreign_path_at(i64, i64) #0 + +; Function Attrs: nounwind +declare ptr @BuildOptions.jni_main_java_source_at(i64, i64) #0 + +; Function Attrs: nounwind +declare i64 @build_options() #0 + +; Function Attrs: nounwind +define internal void @SxFoo.bump(ptr %0, ptr %1) #0 { +entry: + %alloca = alloca ptr, align 8 + store ptr %1, ptr %alloca, align 8 + %load = load ptr, ptr %alloca, align 8 + %gep = getelementptr inbounds { i32 }, ptr %load, i32 0, i32 0 + %loadN = load i32, ptr %gep, align 4 + %add = add i32 %loadN, 1 + store i32 %add, ptr %gep, align 4 + ret void +} + +; Function Attrs: nounwind +define i32 @main() #0 { +entry: + %alloca = alloca { ptr, i64 }, align 8 + %gep = getelementptr inbounds { ptr, i64 }, ptr %alloca, i32 0, i32 0 + store ptr null, ptr %gep, align 8 + %gepN = getelementptr inbounds { ptr, i64 }, ptr %alloca, i32 0, i32 1 + store i64 0, ptr %gepN, align 8 + %allocaN = alloca { ptr, i64 }, align 8 + store { ptr, i64 } { ptr @str.16, i64 9 }, ptr %allocaN, align 8 + %allocaN = alloca { ptr, i64 }, align 8 + store { ptr, i64 } { ptr @str.17, i64 0 }, ptr %allocaN, align 8 + %load = load { ptr, i64 }, ptr %allocaN, align 8 + %loadN = load { ptr, i64 }, ptr %allocaN, align 8 + %call = call { ptr, i64 } @substr(ptr @__sx_default_context, { ptr, i64 } %loadN, i64 0, i64 9) + %callN = call { ptr, i64 } @concat(ptr @__sx_default_context, { ptr, i64 } %load, { ptr, i64 } %call) + store { ptr, i64 } %callN, ptr %allocaN, align 8 + %loadN = load { ptr, i64 }, ptr %allocaN, align 8 + %str.ptr = extractvalue { ptr, i64 } %loadN, 0 + %str.len = extractvalue { ptr, i64 } %loadN, 1 + %0 = call i64 @write(i32 1, ptr %str.ptr, i64 %str.len) + ret i32 0 +} + +; Function Attrs: nounwind +define internal ptr @__thunk_CAllocator_Allocator_alloc(ptr %0, ptr %1, i64 %2) #0 { +entry: + %call = call ptr @CAllocator.alloc(ptr %0, ptr %1, i64 %2) + ret ptr %call +} + +; Function Attrs: nounwind +define internal void @__thunk_CAllocator_Allocator_dealloc(ptr %0, ptr %1, ptr %2) #0 { +entry: + call void @CAllocator.dealloc(ptr %0, ptr %1, ptr %2) + ret void +} + +; Function Attrs: nounwind +define internal { ptr, i64 } @__insert_0(ptr %0) #0 { +entry: + %call = call { ptr, i64 } @build_format(ptr %0, { ptr, i64 } { ptr @str.18, i64 9 }) + ret { ptr, i64 } %call +} + +declare i64 @write(i32, ptr, i64) diff --git a/tests/expected/142-objc-class-method-lowering.txt b/tests/expected/142-objc-class-method-lowering.txt new file mode 100644 index 0000000..e3ca647 --- /dev/null +++ b/tests/expected/142-objc-class-method-lowering.txt @@ -0,0 +1 @@ +compiled