From 37d45aa1015b9a13ebff8e4e12eed15558b5cefb Mon Sep 17 00:00:00 2001
From: David Gonzalez Martin <davidgmbb@gmail.com>
Date: Mon, 24 Mar 2025 14:01:26 +0100
Subject: [PATCH] Implement `pointer_cast`

---
 src/LLVM.zig           |  4 +++
 src/compiler.bbb       | 40 +++++++++++++++++++++++
 src/converter.zig      | 73 ++++++++++++++++++++++++++++++++++++------
 src/converter_test.zig |  4 +++
 src/llvm_api.zig       |  1 +
 tests/pointer_cast.bbb |  7 ++++
 6 files changed, 120 insertions(+), 9 deletions(-)
 create mode 100644 tests/pointer_cast.bbb

diff --git a/src/LLVM.zig b/src/LLVM.zig
index 34671d6..f9dc77c 100644
--- a/src/LLVM.zig
+++ b/src/LLVM.zig
@@ -944,6 +944,10 @@ pub const Builder = opaque {
         return api.LLVMBuildPtrToInt(builder, value, destination_type, "");
     }
 
+    pub fn create_pointer_cast(builder: *Builder, value: *Value, destination_type: *Type) *Value {
+        return api.LLVMBuildPointerCast(builder, value, destination_type, "");
+    }
+
     pub fn create_truncate(builder: *Builder, value: *Value, destination_type: *Type) *Value {
         return api.LLVMBuildTrunc(builder, value, destination_type, "");
     }
diff --git a/src/compiler.bbb b/src/compiler.bbb
index 34a865a..13dc3be 100644
--- a/src/compiler.bbb
+++ b/src/compiler.bbb
@@ -39,6 +39,7 @@ OS_Linux_MAP = bits u32
 }
 
 [extern] mmap = fn [cc(c)] (address: u64, size: u64, protection: OS_Linux_PROT, map: OS_Linux_MAP, file_descriptor: s32, offset: s64) &u8;
+[extern] mprotect = fn [cc(c)] (address: u64, size: u64, protection: OS_Linux_PROT) s32;
 
 OS_ProtectionFlags = bits
 {
@@ -89,6 +90,16 @@ os_reserve = fn (base: u64, size: u64, protection: OS_ProtectionFlags, map: OS_M
     return address;
 }
 
+os_commit = fn (address: u64, size: u64, protection: OS_ProtectionFlags) void
+{
+    >protection_flags = os_linux_protection_flags(protection);
+    >result = mprotect(address, size, protection_flags);
+    if (result != 0)
+    {
+        unreachable;
+    }
+}
+
 Arena = struct
 {
     reserved_size: u64,
@@ -109,6 +120,35 @@ ArenaInitialization = struct
 
 arena_initialize = fn (initialization: ArenaInitialization) &Arena
 {
+    >protection_flags: OS_ProtectionFlags = {
+        .read = 1,
+        .write = 1,
+        zero,
+    };
+
+    >map_flags: OS_MapFlags = {
+        .private = 1,
+        .anonymous = 1,
+        .no_reserve = 1,
+        .populate = 0,
+    };
+
+    >arena: &Arena = #pointer_cast(os_reserve(0, initialization.reserved_size, protection_flags, map_flags));
+    os_commit(#int_from_pointer(arena), initialization.initial_size, {
+        .read = 1,
+        .write = 1,
+        zero,
+    });
+
+    //arena.& = {
+    //    .reserved_size = initialization.reserved_size,
+    //    .os_position = initialization.initial_size,
+    //    .position = minimum_position,
+    //    .granularity = initialization.granularity,
+    //    zero,
+    //};
+
+    return arena;
 }
 
 [export] main = fn [cc(c)] () s32
diff --git a/src/converter.zig b/src/converter.zig
index 6a3088c..b2feb13 100644
--- a/src/converter.zig
+++ b/src/converter.zig
@@ -1114,6 +1114,7 @@ pub const Value = struct {
         return switch (value.bb) {
             .constant_integer, .constant_array => true,
             .struct_initialization => |si| si.is_constant,
+            .instruction => false,
             else => @trap(),
         };
     }
@@ -2203,8 +2204,6 @@ const Converter = struct {
                         .@"return" => {
                             converter.skip_space();
 
-                            const abi_return_type = current_function_type.abi_return_type;
-                            _ = abi_return_type;
                             const return_type_abi = &current_function_type.return_type_abi;
                             const returns_nothing = converter.consume_character_if_match(';');
                             if (returns_nothing) {
@@ -2225,12 +2224,13 @@ const Converter = struct {
                                                 @trap();
                                             },
                                             else => {
+                                                assert(return_value.type.is_abi_equal(return_type_abi.semantic_type));
                                                 const return_alloca = current_function.return_alloca;
                                                 _ = module.create_store(.{
                                                     .source_value = return_value.llvm,
                                                     .destination_value = return_alloca,
                                                     .source_type = return_type_abi.semantic_type,
-                                                    .destination_type = current_function_type.abi_return_type,
+                                                    .destination_type = return_type_abi.semantic_type,
                                                 });
                                             },
                                         }
@@ -2647,6 +2647,7 @@ const Converter = struct {
         integer_max,
         int_from_enum,
         int_from_pointer,
+        pointer_cast,
         select,
         trap,
         truncate,
@@ -2825,6 +2826,30 @@ const Converter = struct {
                 };
                 return value;
             },
+            .pointer_cast => {
+                const ty = expected_type orelse converter.report_error();
+                if (ty.bb != .pointer) {
+                    converter.report_error();
+                }
+                const source_value = converter.parse_value(module, null, .value);
+                converter.skip_space();
+                converter.expect_character(right_parenthesis);
+                if (source_value.type.bb != .pointer) {
+                    converter.report_error();
+                }
+                if (ty == source_value.type) {
+                    converter.report_error();
+                }
+                const value = module.values.add();
+                value.* = .{
+                    .llvm = module.llvm.builder.create_pointer_cast(source_value.llvm, ty.llvm.handle),
+                    .type = ty,
+                    .bb = .instruction,
+                    .lvalue = true,
+                    .dereference_to_assign = false,
+                };
+                return value;
+            },
             .select => {
                 const condition_value = converter.parse_value(module, null, .value);
 
@@ -3237,6 +3262,7 @@ const Converter = struct {
                             if (field_count == struct_type.fields.len) {
                                 converter.report_error();
                             }
+
                             if (is_ordered and is_constant) {
                                 const zero_fields = struct_type.fields[field_count..];
                                 const zero_field_values = field_value_buffer[field_count..][0..zero_fields.len];
@@ -3567,9 +3593,6 @@ const Converter = struct {
                         const appointee_type = variable.value.type.bb.pointer.type;
 
                         if (converter.consume_character_if_match(left_parenthesis)) {
-                            if (value_kind == .pointer) {
-                                converter.report_error();
-                            }
                             const call = converter.parse_call(module, variable.value);
                             break :b call;
                         } else if (converter.consume_character_if_match('.')) {
@@ -3642,15 +3665,47 @@ const Converter = struct {
                                 .pointer => |pointer_type| {
                                     const element_type = pointer_type.type;
                                     if (converter.consume_character_if_match('&')) {
-                                        const load = module.values.add();
-                                        load.* = .{
+                                        const pointer_load = module.values.add();
+                                        pointer_load.* = .{
                                             .llvm = module.create_load(.{ .type = appointee_type, .value = variable.value.llvm }),
                                             .type = appointee_type,
                                             .bb = .instruction,
                                             .lvalue = false,
                                             .dereference_to_assign = false,
                                         };
-                                        break :b load;
+                                        switch (value_kind) {
+                                            .value => {
+                                                if (expected_type) |expected_ty| {
+                                                    if (expected_ty == appointee_type) {
+                                                        @trap();
+                                                    } else {
+                                                        if (appointee_type.bb == .pointer and appointee_type.bb.pointer.type == expected_ty) {
+                                                            const load = module.values.add();
+                                                            load.* = .{
+                                                                .llvm = module.create_load(.{ .type = expected_ty, .value = pointer_load.llvm }),
+                                                                .type = expected_ty,
+                                                                .bb = .instruction,
+                                                                .lvalue = false,
+                                                                .dereference_to_assign = false,
+                                                            };
+                                                            break :b load;
+                                                        } else {
+                                                            converter.report_error();
+                                                        }
+                                                    }
+                                                } else {
+                                                    @trap();
+                                                }
+                                            },
+                                            .maybe_pointer, .pointer => {
+                                                if (expected_type) |expected_ty| {
+                                                    _ = expected_ty;
+                                                    @trap();
+                                                } else {
+                                                    break :b pointer_load;
+                                                }
+                                            },
+                                        }
                                     } else {
                                         switch (element_type.bb) {
                                             .structure => |*struct_type| {
diff --git a/src/converter_test.zig b/src/converter_test.zig
index c13ec17..8ad6fca 100644
--- a/src/converter_test.zig
+++ b/src/converter_test.zig
@@ -408,3 +408,7 @@ test "integer_max" {
 test "unreachable" {
     try invsrc(@src());
 }
+
+test "pointer_cast" {
+    try invsrc(@src());
+}
diff --git a/src/llvm_api.zig b/src/llvm_api.zig
index 0b92417..01f2e69 100644
--- a/src/llvm_api.zig
+++ b/src/llvm_api.zig
@@ -101,6 +101,7 @@ pub extern fn LLVMBuildZExt(builder: *llvm.Builder, value: *llvm.Value, destinat
 pub extern fn LLVMBuildSExt(builder: *llvm.Builder, value: *llvm.Value, destination_type: *llvm.Type, name: [*:0]const u8) *llvm.Value;
 pub extern fn LLVMBuildIntToPtr(builder: *llvm.Builder, value: *llvm.Value, destination_type: *llvm.Type, name: [*:0]const u8) *llvm.Value;
 pub extern fn LLVMBuildPtrToInt(builder: *llvm.Builder, value: *llvm.Value, destination_type: *llvm.Type, name: [*:0]const u8) *llvm.Value;
+pub extern fn LLVMBuildPointerCast(builder: *llvm.Builder, value: *llvm.Value, ty: *llvm.Type, name: [*:0]const u8) *llvm.Value;
 pub extern fn LLVMBuildTrunc(builder: *llvm.Builder, value: *llvm.Value, destination_type: *llvm.Type, name: [*:0]const u8) *llvm.Value;
 
 pub extern fn LLVMSetCurrentDebugLocation2(builder: *llvm.Builder, location: ?*llvm.DI.Location) void;
diff --git a/tests/pointer_cast.bbb b/tests/pointer_cast.bbb
new file mode 100644
index 0000000..1146bea
--- /dev/null
+++ b/tests/pointer_cast.bbb
@@ -0,0 +1,7 @@
+[export] main = fn [cc(c)] () s32
+{
+    >result: u32 = 0;
+    >pointer = &result;
+    >signed_ptr: &s32 = #pointer_cast(pointer);
+    return signed_ptr.&;
+}