Commit ea393b2bca

Robin Voetter <robin@voetter.nl>
2021-08-29 06:08:19
Address Spaces: Implement in LLVM codegen
1 parent e09465f
src/codegen/llvm/bindings.zig
@@ -197,6 +197,9 @@ pub const Module = opaque {
     pub const addFunction = LLVMAddFunction;
     extern fn LLVMAddFunction(*const Module, Name: [*:0]const u8, FunctionTy: *const Type) *const Value;
 
+    pub const addFunctionInAddressSpace = ZigLLVMAddFunctionInAddressSpace;
+    extern fn ZigLLVMAddFunctionInAddressSpace(*const Module, Name: [*:0]const u8, FunctionTy: *const Type, AddressSpace: c_uint) *const Value;
+
     pub const getNamedFunction = LLVMGetNamedFunction;
     extern fn LLVMGetNamedFunction(*const Module, Name: [*:0]const u8) ?*const Value;
 
@@ -209,6 +212,9 @@ pub const Module = opaque {
     pub const addGlobal = LLVMAddGlobal;
     extern fn LLVMAddGlobal(M: *const Module, Ty: *const Type, Name: [*:0]const u8) *const Value;
 
+    pub const addGlobalInAddressSpace = LLVMAddGlobalInAddressSpace;
+    extern fn LLVMAddGlobalInAddressSpace(M: *const Module, Ty: *const Type, Name: [*:0]const u8, AddressSpace: c_uint) *const Value;
+
     pub const getNamedGlobal = LLVMGetNamedGlobal;
     extern fn LLVMGetNamedGlobal(M: *const Module, Name: [*:0]const u8) ?*const Value;
 
@@ -975,6 +981,8 @@ pub const TypeKind = enum(c_int) {
 };
 
 pub const address_space = struct {
+    pub const default = 0;
+
     // See llvm/lib/Target/X86/X86.h
     pub const x86_64 = x86;
     pub const x86 = struct {
src/codegen/c.zig
@@ -251,7 +251,7 @@ pub const DeclGen = struct {
                     try writer.writeByte('(');
                     try dg.renderType(writer, t);
                     try writer.writeAll("){");
-                    var buf: Type.Payload.ElemType = undefined;
+                    var buf: Type.SlicePtrFieldTypeBuffer = undefined;
                     try dg.renderValue(writer, t.slicePtrFieldType(&buf), val);
                     try writer.writeAll(", ");
                     try writer.print("{d}", .{val.sliceLen()});
src/codegen/llvm.zig
@@ -554,7 +554,8 @@ pub const DeclGen = struct {
             @intCast(c_uint, fn_param_len),
             .False,
         );
-        const llvm_fn = self.llvmModule().addFunction(decl.name, fn_type);
+        const llvm_addrspace = self.llvmAddressSpace(decl.@"addrspace");
+        const llvm_fn = self.llvmModule().addFunctionInAddressSpace(decl.name, fn_type, llvm_addrspace);
 
         const is_extern = decl.val.tag() == .extern_fn;
         if (!is_extern) {
@@ -576,7 +577,27 @@ pub const DeclGen = struct {
         if (llvm_module.getNamedGlobal(decl.name)) |val| return val;
         // TODO: remove this redundant `llvmType`, it is also called in `genTypedValue`.
         const llvm_type = try self.llvmType(decl.ty);
-        return llvm_module.addGlobal(llvm_type, decl.name);
+        const llvm_addrspace = self.llvmAddressSpace(decl.@"addrspace");
+        return llvm_module.addGlobalInAddressSpace(llvm_type, decl.name, llvm_addrspace);
+    }
+
+    fn llvmAddressSpace(self: DeclGen, address_space: std.builtin.AddressSpace) c_uint {
+        const target = self.module.getTarget();
+        return switch (address_space) {
+            .generic => llvm.address_space.default,
+            .gs => switch (target.cpu.arch) {
+                .i386, .x86_64 => llvm.address_space.x86.gs,
+                else => unreachable,
+            },
+            .fs => switch (target.cpu.arch) {
+                .i386, .x86_64 => llvm.address_space.x86.fs,
+                else => unreachable,
+            },
+            .ss => switch (target.cpu.arch) {
+                .i386, .x86_64 => llvm.address_space.x86.ss,
+                else => unreachable,
+            },
+        };
     }
 
     fn llvmType(self: *DeclGen, t: Type) error{ OutOfMemory, CodegenFail }!*const llvm.Type {
@@ -605,7 +626,7 @@ pub const DeclGen = struct {
             .Bool => return self.context.intType(1),
             .Pointer => {
                 if (t.isSlice()) {
-                    var buf: Type.Payload.ElemType = undefined;
+                    var buf: Type.SlicePtrFieldTypeBuffer = undefined;
                     const ptr_type = t.slicePtrFieldType(&buf);
 
                     const fields: [2]*const llvm.Type = .{
@@ -615,7 +636,8 @@ pub const DeclGen = struct {
                     return self.context.structType(&fields, fields.len, .False);
                 } else {
                     const elem_type = try self.llvmType(t.elemType());
-                    return elem_type.pointerType(0);
+                    const llvm_addrspace = self.llvmAddressSpace(t.ptrAddressSpace());
+                    return elem_type.pointerType(llvm_addrspace);
                 }
             },
             .Array => {
@@ -681,7 +703,8 @@ pub const DeclGen = struct {
                     @intCast(c_uint, llvm_params.len),
                     llvm.Bool.fromBool(is_var_args),
                 );
-                return llvm_fn_ty.pointerType(0);
+                const llvm_addrspace = self.llvmAddressSpace(t.fnAddressSpace());
+                return llvm_fn_ty.pointerType(llvm_addrspace);
             },
             .ComptimeInt => unreachable,
             .ComptimeFloat => unreachable,
@@ -749,7 +772,7 @@ pub const DeclGen = struct {
             .Pointer => switch (tv.val.tag()) {
                 .decl_ref => {
                     if (tv.ty.isSlice()) {
-                        var buf: Type.Payload.ElemType = undefined;
+                        var buf: Type.SlicePtrFieldTypeBuffer = undefined;
                         const ptr_ty = tv.ty.slicePtrFieldType(&buf);
                         var slice_len: Value.Payload.U64 = .{
                             .base = .{ .tag = .int_u64 },
@@ -779,12 +802,13 @@ pub const DeclGen = struct {
                     decl.alive = true;
                     const val = try self.resolveGlobalDecl(decl);
                     const llvm_var_type = try self.llvmType(tv.ty);
-                    const llvm_type = llvm_var_type.pointerType(0);
+                    const llvm_addrspace = self.llvmAddressSpace(decl.@"addrspace");
+                    const llvm_type = llvm_var_type.pointerType(llvm_addrspace);
                     return val.constBitCast(llvm_type);
                 },
                 .slice => {
                     const slice = tv.val.castTag(.slice).?.data;
-                    var buf: Type.Payload.ElemType = undefined;
+                    var buf: Type.SlicePtrFieldTypeBuffer = undefined;
                     const fields: [2]*const llvm.Value = .{
                         try self.genTypedValue(.{
                             .ty = tv.ty.slicePtrFieldType(&buf),
src/codegen.zig
@@ -4873,7 +4873,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
             switch (typed_value.ty.zigTypeTag()) {
                 .Pointer => switch (typed_value.ty.ptrSize()) {
                     .Slice => {
-                        var buf: Type.Payload.ElemType = undefined;
+                        var buf: Type.SlicePtrFieldTypeBuffer = undefined;
                         const ptr_type = typed_value.ty.slicePtrFieldType(&buf);
                         const ptr_mcv = try self.genTypedValue(.{ .ty = ptr_type, .val = typed_value.val });
                         const slice_len = typed_value.val.sliceLen();
src/Module.zig
@@ -4761,7 +4761,7 @@ pub fn populateTestFunctions(mod: *Module) !void {
     const builtin_file = (mod.importPkg(builtin_pkg) catch unreachable).file;
     const builtin_namespace = builtin_file.root_decl.?.namespace;
     const decl = builtin_namespace.decls.get("test_functions").?;
-    var buf: Type.Payload.ElemType = undefined;
+    var buf: Type.SlicePtrFieldTypeBuffer = undefined;
     const tmp_test_fn_ty = decl.ty.slicePtrFieldType(&buf).elemType();
 
     const array_decl = d: {
src/Sema.zig
@@ -8390,7 +8390,7 @@ fn fieldVal(
         .Pointer => switch (object_ty.ptrSize()) {
             .Slice => {
                 if (mem.eql(u8, field_name, "ptr")) {
-                    const buf = try arena.create(Type.Payload.ElemType);
+                    const buf = try arena.create(Type.SlicePtrFieldTypeBuffer);
                     const result_ty = object_ty.slicePtrFieldType(buf);
                     if (try sema.resolveMaybeUndefVal(block, object_src, object)) |val| {
                         if (val.isUndef()) return sema.addConstUndef(result_ty);
src/type.zig
@@ -2161,42 +2161,72 @@ pub const Type = extern union {
         };
     }
 
-    pub fn slicePtrFieldType(self: Type, buffer: *Payload.ElemType) Type {
+    pub const SlicePtrFieldTypeBuffer = union {
+        elem_type: Payload.ElemType,
+        pointer: Payload.Pointer,
+    };
+
+    pub fn slicePtrFieldType(self: Type, buffer: *SlicePtrFieldTypeBuffer) Type {
         switch (self.tag()) {
             .const_slice_u8 => return Type.initTag(.manyptr_const_u8),
 
             .const_slice => {
                 const elem_type = self.castTag(.const_slice).?.data;
-                buffer.* = .{
+                buffer.elem_type = .{
                     .base = .{ .tag = .many_const_pointer },
                     .data = elem_type,
                 };
-                return Type.initPayload(&buffer.base);
+                return Type.initPayload(&buffer.elem_type.base);
             },
             .mut_slice => {
                 const elem_type = self.castTag(.mut_slice).?.data;
-                buffer.* = .{
+                buffer.elem_type = .{
                     .base = .{ .tag = .many_mut_pointer },
                     .data = elem_type,
                 };
-                return Type.initPayload(&buffer.base);
+                return Type.initPayload(&buffer.elem_type.base);
             },
 
             .pointer => {
                 const payload = self.castTag(.pointer).?.data;
                 assert(payload.size == .Slice);
-                if (payload.mutable) {
-                    buffer.* = .{
+
+                if (payload.sentinel != null or
+                    payload.@"align" != 0 or
+                    payload.@"addrspace" != .generic or
+                    payload.bit_offset != 0 or
+                    payload.host_size != 0 or
+                    payload.@"allowzero" or
+                    payload.@"volatile"
+                ) {
+                    buffer.pointer = .{
+                        .data = .{
+                            .pointee_type = payload.pointee_type,
+                            .sentinel = payload.sentinel,
+                            .@"align" = payload.@"align",
+                            .@"addrspace" = payload.@"addrspace",
+                            .bit_offset = payload.bit_offset,
+                            .host_size = payload.host_size,
+                            .@"allowzero" = payload.@"allowzero",
+                            .mutable = payload.mutable,
+                            .@"volatile" = payload.@"volatile",
+                            .size = .Many
+                        },
+                    };
+                    return Type.initPayload(&buffer.pointer.base);
+                } else if (payload.mutable) {
+                    buffer.elem_type = .{
                         .base = .{ .tag = .many_mut_pointer },
                         .data = payload.pointee_type,
                     };
+                    return Type.initPayload(&buffer.elem_type.base);
                 } else {
-                    buffer.* = .{
+                    buffer.elem_type = .{
                         .base = .{ .tag = .many_const_pointer },
                         .data = payload.pointee_type,
                     };
+                    return Type.initPayload(&buffer.elem_type.base);
                 }
-                return Type.initPayload(&buffer.base);
             },
 
             else => unreachable,
src/zig_llvm.cpp
@@ -416,6 +416,11 @@ ZIG_EXTERN_C LLVMTypeRef ZigLLVMTokenTypeInContext(LLVMContextRef context_ref) {
   return wrap(Type::getTokenTy(*unwrap(context_ref)));
 }
 
+LLVMValueRef ZigLLVMAddFunctionInAddressSpace(LLVMModuleRef M, const char *Name, LLVMTypeRef FunctionTy, unsigned AddressSpace) {
+    Function* func = Function::Create(unwrap<FunctionType>(FunctionTy), GlobalValue::ExternalLinkage, AddressSpace, Name, unwrap(M));
+    return wrap(func);
+}
+
 LLVMValueRef ZigLLVMBuildCall(LLVMBuilderRef B, LLVMValueRef Fn, LLVMValueRef *Args,
         unsigned NumArgs, ZigLLVM_CallingConv CC, ZigLLVM_CallAttr attr, const char *Name)
 {
src/zig_llvm.h
@@ -65,6 +65,9 @@ ZIG_EXTERN_C LLVMTargetMachineRef ZigLLVMCreateTargetMachine(LLVMTargetRef T, co
 
 ZIG_EXTERN_C LLVMTypeRef ZigLLVMTokenTypeInContext(LLVMContextRef context_ref);
 
+ZIG_EXTERN_C LLVMValueRef ZigLLVMAddFunctionInAddressSpace(LLVMModuleRef M, const char *Name,
+        LLVMTypeRef FunctionTy, unsigned AddressSpace);
+
 enum ZigLLVM_CallingConv {
     ZigLLVM_C = 0,
     ZigLLVM_Fast = 8,