authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-10-11 11:00:32-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-10-11 11:39:12-07:00
log6d6cf598475ab8d2c3259002655ba04f1d056b2e
treedaf38d2c3c68de0411d9106668bde92cc7e0651b
parentf42725c39bbbe5db13c1a1706db3f31aa0549307

stage2: support nested structs and arrays and sret

* Add AIR instructions: ret_ptr, ret_load - This allows Sema to be blissfully unaware of the backend's decision to implement by-val/by-ref semantics for struct/union/array types. Backends can lower these simply as alloc, load, ret instructions, or they can take advantage of them to use a result pointer. * Add AIR instruction: array_elem_val - Allows for better codegen for `Sema.elemVal`. * Implement calculation of ABI alignment and ABI size for unions. * Before appending the following AIR instructions to a block, resolveTypeLayout is called on the type: - call - return type - ret - return type - store_ptr - elem type * Sema: fix memory leak in `zirArrayInit` and other cleanups to this function. * x86_64: implement the full x86_64 C ABI according to the spec * Type: implement `intInfo` for error sets. * Type: implement `intTagType` for tagged unions. The Zig type tag `Fn` is now used exclusively for function bodies. Function pointers are modeled as `*const T` where `T` is a `Fn` type. * The `call` AIR instruction now allows a function pointer operand as well as a function operand. * Sema now has a coercion from function body to function pointer. * Function type syntax, e.g. `fn()void`, now returns zig tag type of Pointer with child Fn, rather than Fn directly. - I think this should probably be reverted. Will discuss the lang specs before doing this. Idea being that function pointers would need to be specified as `*const fn()void` rather than `fn() void`. LLVM backend: * Enable calling the panic handler (previously this just emitted `@breakpoint()` since the backend could not handle the panic function). * Implement sret * Introduce `isByRef` and implement it for structs and arrays. Types that are `isByRef` are now passed as pointers to functions, and e.g. `elem_val` will return a pointer instead of doing a load. * Move the function type creating code from `resolveLlvmFunction` to `llvmType` where it belongs; now there is only 1 instance of this logic instead of two. * Add the `nonnull` attribute to non-optional pointer parameters. * Fix `resolveGlobalDecl` not using fully-qualified names and not using the `decl_map`. * Implement `genTypedValue` for pointer-like optionals. * Fix memory leak when lowering `block` instruction and OOM occurs. * Implement volatile checks where relevant.

17 files changed, 1173 insertions(+), 370 deletions(-)

src/Air.zig+28-3
...@@ -110,6 +110,10 @@ pub const Inst = struct {...@@ -110,6 +110,10 @@ pub const Inst = struct {
110 /// Allocates stack local memory.110 /// Allocates stack local memory.
111 /// Uses the `ty` field.111 /// Uses the `ty` field.
112 alloc,112 alloc,
113 /// If the function will pass the result by-ref, this instruction returns the
114 /// result pointer. Otherwise it is equivalent to `alloc`.
115 /// Uses the `ty` field.
116 ret_ptr,
113 /// Inline assembly. Uses the `ty_pl` field. Payload is `Asm`.117 /// Inline assembly. Uses the `ty_pl` field. Payload is `Asm`.
114 assembly,118 assembly,
115 /// Bitwise AND. `&`.119 /// Bitwise AND. `&`.
...@@ -160,6 +164,7 @@ pub const Inst = struct {...@@ -160,6 +164,7 @@ pub const Inst = struct {
160 /// Function call.164 /// Function call.
161 /// Result type is the return type of the function being called.165 /// Result type is the return type of the function being called.
162 /// Uses the `pl_op` field with the `Call` payload. operand is the callee.166 /// Uses the `pl_op` field with the `Call` payload. operand is the callee.
167 /// Triggers `resolveTypeLayout` on the return type of the callee.
163 call,168 call,
164 /// Count leading zeroes of an integer according to its representation in twos complement.169 /// Count leading zeroes of an integer according to its representation in twos complement.
165 /// Result type will always be an unsigned integer big enough to fit the answer.170 /// Result type will always be an unsigned integer big enough to fit the answer.
...@@ -257,7 +262,16 @@ pub const Inst = struct {...@@ -257,7 +262,16 @@ pub const Inst = struct {
257 /// Return a value from a function.262 /// Return a value from a function.
258 /// Result type is always noreturn; no instructions in a block follow this one.263 /// Result type is always noreturn; no instructions in a block follow this one.
259 /// Uses the `un_op` field.264 /// Uses the `un_op` field.
265 /// Triggers `resolveTypeLayout` on the return type.
260 ret,266 ret,
267 /// This instruction communicates that the function's result value is inside
268 /// the operand, which is a pointer. If the function will pass the result by-ref,
269 /// the pointer operand is a `ret_ptr` instruction. Otherwise, this instruction
270 /// is equivalent to a `load` on the operand, followed by a `ret` on the loaded value.
271 /// Result type is always noreturn; no instructions in a block follow this one.
272 /// Uses the `un_op` field.
273 /// Triggers `resolveTypeLayout` on the return type.
274 ret_load,
261 /// Write a value to a pointer. LHS is pointer, RHS is value.275 /// Write a value to a pointer. LHS is pointer, RHS is value.
262 /// Result type is always void.276 /// Result type is always void.
263 /// Uses the `bin_op` field.277 /// Uses the `bin_op` field.
...@@ -341,6 +355,10 @@ pub const Inst = struct {...@@ -341,6 +355,10 @@ pub const Inst = struct {
341 /// Given a slice value, return the pointer.355 /// Given a slice value, return the pointer.
342 /// Uses the `ty_op` field.356 /// Uses the `ty_op` field.
343 slice_ptr,357 slice_ptr,
358 /// Given an array value and element index, return the element value at that index.
359 /// Result type is the element type of the array operand.
360 /// Uses the `bin_op` field.
361 array_elem_val,
344 /// Given a slice value, and element index, return the element value at that index.362 /// Given a slice value, and element index, return the element value at that index.
345 /// Result type is the element type of the slice operand.363 /// Result type is the element type of the slice operand.
346 /// Uses the `bin_op` field.364 /// Uses the `bin_op` field.
...@@ -644,7 +662,9 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {...@@ -644,7 +662,9 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
644662
645 .const_ty => return Type.initTag(.type),663 .const_ty => return Type.initTag(.type),
646664
647 .alloc => return datas[inst].ty,665 .alloc,
666 .ret_ptr,
667 => return datas[inst].ty,
648668
649 .assembly,669 .assembly,
650 .block,670 .block,
...@@ -690,6 +710,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {...@@ -690,6 +710,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
690 .cond_br,710 .cond_br,
691 .switch_br,711 .switch_br,
692 .ret,712 .ret,
713 .ret_load,
693 .unreach,714 .unreach,
694 => return Type.initTag(.noreturn),715 => return Type.initTag(.noreturn),
695716
...@@ -714,10 +735,14 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {...@@ -714,10 +735,14 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
714735
715 .call => {736 .call => {
716 const callee_ty = air.typeOf(datas[inst].pl_op.operand);737 const callee_ty = air.typeOf(datas[inst].pl_op.operand);
717 return callee_ty.fnReturnType();738 switch (callee_ty.zigTypeTag()) {
739 .Fn => return callee_ty.fnReturnType(),
740 .Pointer => return callee_ty.childType().fnReturnType(),
741 else => unreachable,
742 }
718 },743 },
719744
720 .slice_elem_val, .ptr_elem_val => {745 .slice_elem_val, .ptr_elem_val, .array_elem_val => {
721 const ptr_ty = air.typeOf(datas[inst].bin_op.lhs);746 const ptr_ty = air.typeOf(datas[inst].bin_op.lhs);
722 return ptr_ty.elemType();747 return ptr_ty.elemType();
723 },748 },
src/Liveness.zig+3
...@@ -250,6 +250,7 @@ fn analyzeInst(...@@ -250,6 +250,7 @@ fn analyzeInst(
250 .bool_and,250 .bool_and,
251 .bool_or,251 .bool_or,
252 .store,252 .store,
253 .array_elem_val,
253 .slice_elem_val,254 .slice_elem_val,
254 .ptr_slice_elem_val,255 .ptr_slice_elem_val,
255 .ptr_elem_val,256 .ptr_elem_val,
...@@ -270,6 +271,7 @@ fn analyzeInst(...@@ -270,6 +271,7 @@ fn analyzeInst(
270271
271 .arg,272 .arg,
272 .alloc,273 .alloc,
274 .ret_ptr,
273 .constant,275 .constant,
274 .const_ty,276 .const_ty,
275 .breakpoint,277 .breakpoint,
...@@ -322,6 +324,7 @@ fn analyzeInst(...@@ -322,6 +324,7 @@ fn analyzeInst(
322 .ptrtoint,324 .ptrtoint,
323 .bool_to_int,325 .bool_to_int,
324 .ret,326 .ret,
327 .ret_load,
325 => {328 => {
326 const operand = inst_datas[inst].un_op;329 const operand = inst_datas[inst].un_op;
327 return trackOperands(a, new_set, inst, main_tomb, .{ operand, .none, .none });330 return trackOperands(a, new_set, inst, main_tomb, .{ operand, .none, .none });
src/Module.zig+78-7
...@@ -785,7 +785,7 @@ pub const Struct = struct {...@@ -785,7 +785,7 @@ pub const Struct = struct {
785 /// The Decl that corresponds to the struct itself.785 /// The Decl that corresponds to the struct itself.
786 owner_decl: *Decl,786 owner_decl: *Decl,
787 /// Set of field names in declaration order.787 /// Set of field names in declaration order.
788 fields: std.StringArrayHashMapUnmanaged(Field),788 fields: Fields,
789 /// Represents the declarations inside this struct.789 /// Represents the declarations inside this struct.
790 namespace: Namespace,790 namespace: Namespace,
791 /// Offset from `owner_decl`, points to the struct AST node.791 /// Offset from `owner_decl`, points to the struct AST node.
...@@ -805,6 +805,8 @@ pub const Struct = struct {...@@ -805,6 +805,8 @@ pub const Struct = struct {
805 /// is necessary to determine whether it has bits at runtime.805 /// is necessary to determine whether it has bits at runtime.
806 known_has_bits: bool,806 known_has_bits: bool,
807807
808 pub const Fields = std.StringArrayHashMapUnmanaged(Field);
809
808 /// The `Type` and `Value` memory is owned by the arena of the Struct's owner_decl.810 /// The `Type` and `Value` memory is owned by the arena of the Struct's owner_decl.
809 pub const Field = struct {811 pub const Field = struct {
810 /// Uses `noreturn` to indicate `anytype`.812 /// Uses `noreturn` to indicate `anytype`.
...@@ -935,7 +937,7 @@ pub const Union = struct {...@@ -935,7 +937,7 @@ pub const Union = struct {
935 /// This will be set to the null type until status is `have_field_types`.937 /// This will be set to the null type until status is `have_field_types`.
936 tag_ty: Type,938 tag_ty: Type,
937 /// Set of field names in declaration order.939 /// Set of field names in declaration order.
938 fields: std.StringArrayHashMapUnmanaged(Field),940 fields: Fields,
939 /// Represents the declarations inside this union.941 /// Represents the declarations inside this union.
940 namespace: Namespace,942 namespace: Namespace,
941 /// Offset from `owner_decl`, points to the union decl AST node.943 /// Offset from `owner_decl`, points to the union decl AST node.
...@@ -958,6 +960,8 @@ pub const Union = struct {...@@ -958,6 +960,8 @@ pub const Union = struct {
958 abi_align: Value,960 abi_align: Value,
959 };961 };
960962
963 pub const Fields = std.StringArrayHashMapUnmanaged(Field);
964
961 pub fn getFullyQualifiedName(s: *Union, gpa: *Allocator) ![]u8 {965 pub fn getFullyQualifiedName(s: *Union, gpa: *Allocator) ![]u8 {
962 return s.owner_decl.getFullyQualifiedName(gpa);966 return s.owner_decl.getFullyQualifiedName(gpa);
963 }967 }
...@@ -992,14 +996,18 @@ pub const Union = struct {...@@ -992,14 +996,18 @@ pub const Union = struct {
992996
993 pub fn mostAlignedField(u: Union, target: Target) u32 {997 pub fn mostAlignedField(u: Union, target: Target) u32 {
994 assert(u.haveFieldTypes());998 assert(u.haveFieldTypes());
995 var most_alignment: u64 = 0;999 var most_alignment: u32 = 0;
996 var most_index: usize = undefined;1000 var most_index: usize = undefined;
997 for (u.fields.values()) |field, i| {1001 for (u.fields.values()) |field, i| {
998 if (!field.ty.hasCodeGenBits()) continue;1002 if (!field.ty.hasCodeGenBits()) continue;
999 const field_align = if (field.abi_align.tag() == .abi_align_default)1003
1000 field.ty.abiAlignment(target)1004 const field_align = a: {
1001 else1005 if (field.abi_align.tag() == .abi_align_default) {
1002 field.abi_align.toUnsignedInt();1006 break :a field.ty.abiAlignment(target);
1007 } else {
1008 break :a @intCast(u32, field.abi_align.toUnsignedInt());
1009 }
1010 };
1003 if (field_align > most_alignment) {1011 if (field_align > most_alignment) {
1004 most_alignment = field_align;1012 most_alignment = field_align;
1005 most_index = i;1013 most_index = i;
...@@ -1007,6 +1015,69 @@ pub const Union = struct {...@@ -1007,6 +1015,69 @@ pub const Union = struct {
1007 }1015 }
1008 return @intCast(u32, most_index);1016 return @intCast(u32, most_index);
1009 }1017 }
1018
1019 pub fn abiAlignment(u: Union, target: Target, have_tag: bool) u32 {
1020 var max_align: u32 = 0;
1021 if (have_tag) max_align = u.tag_ty.abiAlignment(target);
1022 for (u.fields.values()) |field| {
1023 if (!field.ty.hasCodeGenBits()) continue;
1024
1025 const field_align = a: {
1026 if (field.abi_align.tag() == .abi_align_default) {
1027 break :a field.ty.abiAlignment(target);
1028 } else {
1029 break :a @intCast(u32, field.abi_align.toUnsignedInt());
1030 }
1031 };
1032 max_align = @maximum(max_align, field_align);
1033 }
1034 assert(max_align != 0);
1035 return max_align;
1036 }
1037
1038 pub fn abiSize(u: Union, target: Target, have_tag: bool) u64 {
1039 assert(u.haveFieldTypes());
1040 const is_packed = u.layout == .Packed;
1041 if (is_packed) @panic("TODO packed unions");
1042
1043 var payload_size: u64 = 0;
1044 var payload_align: u32 = 0;
1045 for (u.fields.values()) |field| {
1046 if (!field.ty.hasCodeGenBits()) continue;
1047
1048 const field_align = a: {
1049 if (field.abi_align.tag() == .abi_align_default) {
1050 break :a field.ty.abiAlignment(target);
1051 } else {
1052 break :a @intCast(u32, field.abi_align.toUnsignedInt());
1053 }
1054 };
1055 payload_size = @maximum(payload_size, field.ty.abiSize(target));
1056 payload_align = @maximum(payload_align, field_align);
1057 }
1058 if (!have_tag) {
1059 return std.mem.alignForwardGeneric(u64, payload_size, payload_align);
1060 }
1061 // Put the tag before or after the payload depending on which one's
1062 // alignment is greater.
1063 const tag_size = u.tag_ty.abiSize(target);
1064 const tag_align = u.tag_ty.abiAlignment(target);
1065 var size: u64 = 0;
1066 if (tag_align >= payload_align) {
1067 // {Tag, Payload}
1068 size += tag_size;
1069 size = std.mem.alignForwardGeneric(u64, size, payload_align);
1070 size += payload_size;
1071 size = std.mem.alignForwardGeneric(u64, size, tag_align);
1072 } else {
1073 // {Payload, Tag}
1074 size += payload_size;
1075 size = std.mem.alignForwardGeneric(u64, size, tag_align);
1076 size += tag_size;
1077 size = std.mem.alignForwardGeneric(u64, size, payload_align);
1078 }
1079 return size;
1080 }
1010};1081};
10111082
1012/// Some Fn struct memory is owned by the Decl's TypedValue.Managed arena allocator.1083/// Some Fn struct memory is owned by the Decl's TypedValue.Managed arena allocator.
src/Sema.zig+119-72
...@@ -1814,7 +1814,7 @@ fn zirRetPtr(...@@ -1814,7 +1814,7 @@ fn zirRetPtr(
1814 .pointee_type = sema.fn_ret_ty,1814 .pointee_type = sema.fn_ret_ty,
1815 .@"addrspace" = target_util.defaultAddressSpace(sema.mod.getTarget(), .local),1815 .@"addrspace" = target_util.defaultAddressSpace(sema.mod.getTarget(), .local),
1816 });1816 });
1817 return block.addTy(.alloc, ptr_type);1817 return block.addTy(.ret_ptr, ptr_type);
1818}1818}
18191819
1820fn zirRef(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {1820fn zirRef(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
...@@ -3331,9 +3331,20 @@ fn analyzeCall(...@@ -3331,9 +3331,20 @@ fn analyzeCall(
3331) CompileError!Air.Inst.Ref {3331) CompileError!Air.Inst.Ref {
3332 const mod = sema.mod;3332 const mod = sema.mod;
33333333
3334 const func_ty = sema.typeOf(func);3334 const callee_ty = sema.typeOf(func);
3335 if (func_ty.zigTypeTag() != .Fn)3335 const func_ty = func_ty: {
3336 return sema.fail(block, func_src, "type '{}' not a function", .{func_ty});3336 switch (callee_ty.zigTypeTag()) {
3337 .Fn => break :func_ty callee_ty,
3338 .Pointer => {
3339 const ptr_info = callee_ty.ptrInfo().data;
3340 if (ptr_info.size == .One and ptr_info.pointee_type.zigTypeTag() == .Fn) {
3341 break :func_ty ptr_info.pointee_type;
3342 }
3343 },
3344 else => {},
3345 }
3346 return sema.fail(block, func_src, "type '{}' not a function", .{callee_ty});
3347 };
33373348
3338 const func_ty_info = func_ty.fnInfo();3349 const func_ty_info = func_ty.fnInfo();
3339 const cc = func_ty_info.cc;3350 const cc = func_ty_info.cc;
...@@ -3393,6 +3404,7 @@ fn analyzeCall(...@@ -3393,6 +3404,7 @@ fn analyzeCall(
3393 const result: Air.Inst.Ref = if (is_inline_call) res: {3404 const result: Air.Inst.Ref = if (is_inline_call) res: {
3394 const func_val = try sema.resolveConstValue(block, func_src, func);3405 const func_val = try sema.resolveConstValue(block, func_src, func);
3395 const module_fn = switch (func_val.tag()) {3406 const module_fn = switch (func_val.tag()) {
3407 .decl_ref => func_val.castTag(.decl_ref).?.data.val.castTag(.function).?.data,
3396 .function => func_val.castTag(.function).?.data,3408 .function => func_val.castTag(.function).?.data,
3397 .extern_fn => return sema.fail(block, call_src, "{s} call of extern function", .{3409 .extern_fn => return sema.fail(block, call_src, "{s} call of extern function", .{
3398 @as([]const u8, if (is_comptime_call) "comptime" else "inline"),3410 @as([]const u8, if (is_comptime_call) "comptime" else "inline"),
...@@ -3610,7 +3622,11 @@ fn analyzeCall(...@@ -3610,7 +3622,11 @@ fn analyzeCall(
3610 break :res res2;3622 break :res res2;
3611 } else if (func_ty_info.is_generic) res: {3623 } else if (func_ty_info.is_generic) res: {
3612 const func_val = try sema.resolveConstValue(block, func_src, func);3624 const func_val = try sema.resolveConstValue(block, func_src, func);
3613 const module_fn = func_val.castTag(.function).?.data;3625 const module_fn = switch (func_val.tag()) {
3626 .function => func_val.castTag(.function).?.data,
3627 .decl_ref => func_val.castTag(.decl_ref).?.data.val.castTag(.function).?.data,
3628 else => unreachable,
3629 };
3614 // Check the Module's generic function map with an adapted context, so that we3630 // Check the Module's generic function map with an adapted context, so that we
3615 // can match against `uncasted_args` rather than doing the work below to create a3631 // can match against `uncasted_args` rather than doing the work below to create a
3616 // generic Scope only to junk it if it matches an existing instantiation.3632 // generic Scope only to junk it if it matches an existing instantiation.
...@@ -3880,6 +3896,8 @@ fn analyzeCall(...@@ -3880,6 +3896,8 @@ fn analyzeCall(
3880 }3896 }
38813897
3882 try sema.requireRuntimeBlock(block, call_src);3898 try sema.requireRuntimeBlock(block, call_src);
3899 try sema.resolveTypeLayout(block, call_src, func_ty_info.return_type);
3900
3883 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Call).Struct.fields.len +3901 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Call).Struct.fields.len +
3884 args.len);3902 args.len);
3885 const func_inst = try block.addInst(.{3903 const func_inst = try block.addInst(.{
...@@ -3954,6 +3972,8 @@ fn finishGenericCall(...@@ -3954,6 +3972,8 @@ fn finishGenericCall(
3954 }3972 }
3955 total_i += 1;3973 total_i += 1;
3956 }3974 }
3975
3976 try sema.resolveTypeLayout(block, call_src, new_fn_ty.fnReturnType());
3957 }3977 }
3958 try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.Call).Struct.fields.len +3978 try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.Call).Struct.fields.len +
3959 runtime_args_len);3979 runtime_args_len);
...@@ -4787,7 +4807,12 @@ fn funcCommon(...@@ -4787,7 +4807,12 @@ fn funcCommon(
4787 }4807 }
47884808
4789 if (body_inst == 0) {4809 if (body_inst == 0) {
4790 return sema.addType(fn_ty);4810 const fn_ptr_ty = try Type.ptr(sema.arena, .{
4811 .pointee_type = fn_ty,
4812 .@"addrspace" = .generic,
4813 .mutable = false,
4814 });
4815 return sema.addType(fn_ptr_ty);
4791 }4816 }
47924817
4793 const is_inline = fn_ty.fnCallingConvention() == .Inline;4818 const is_inline = fn_ty.fnCallingConvention() == .Inline;
...@@ -8366,13 +8391,15 @@ fn zirRetLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Zir...@@ -8366,13 +8391,15 @@ fn zirRetLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Zir
83668391
8367 const inst_data = sema.code.instructions.items(.data)[inst].un_node;8392 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
8368 const src = inst_data.src();8393 const src = inst_data.src();
8369 // TODO: when implementing functions that accept a result location pointer,
8370 // this logic will be updated to only do a load in case that the function's return
8371 // type in fact does not need a result location pointer. Until then we assume
8372 // the `ret_ptr` is the same as an `alloc` and do a load here.
8373 const ret_ptr = sema.resolveInst(inst_data.operand);8394 const ret_ptr = sema.resolveInst(inst_data.operand);
8374 const operand = try sema.analyzeLoad(block, src, ret_ptr, src);8395
8375 return sema.analyzeRet(block, operand, src, false);8396 if (block.is_comptime or block.inlining != null) {
8397 const operand = try sema.analyzeLoad(block, src, ret_ptr, src);
8398 return sema.analyzeRet(block, operand, src, false);
8399 }
8400 try sema.requireRuntimeBlock(block, src);
8401 _ = try block.addUnOp(.ret_load, ret_ptr);
8402 return always_noreturn;
8376}8403}
83778404
8378fn analyzeRet(8405fn analyzeRet(
...@@ -8398,6 +8425,7 @@ fn analyzeRet(...@@ -8398,6 +8425,7 @@ fn analyzeRet(
8398 return always_noreturn;8425 return always_noreturn;
8399 }8426 }
84008427
8428 try sema.resolveTypeLayout(block, src, sema.fn_ret_ty);
8401 _ = try block.addUnOp(.ret, operand);8429 _ = try block.addUnOp(.ret, operand);
8402 return always_noreturn;8430 return always_noreturn;
8403}8431}
...@@ -8653,56 +8681,76 @@ fn zirStructInitAnon(sema: *Sema, block: *Block, inst: Zir.Inst.Index, is_ref: b...@@ -8653,56 +8681,76 @@ fn zirStructInitAnon(sema: *Sema, block: *Block, inst: Zir.Inst.Index, is_ref: b
8653 return sema.fail(block, src, "TODO: Sema.zirStructInitAnon", .{});8681 return sema.fail(block, src, "TODO: Sema.zirStructInitAnon", .{});
8654}8682}
86558683
8656fn zirArrayInit(sema: *Sema, block: *Block, inst: Zir.Inst.Index, is_ref: bool) CompileError!Air.Inst.Ref {8684fn zirArrayInit(
8685 sema: *Sema,
8686 block: *Block,
8687 inst: Zir.Inst.Index,
8688 is_ref: bool,
8689) CompileError!Air.Inst.Ref {
8690 const gpa = sema.gpa;
8657 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;8691 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
8658 const src = inst_data.src();8692 const src = inst_data.src();
86598693
8660 const extra = sema.code.extraData(Zir.Inst.MultiOp, inst_data.payload_index);8694 const extra = sema.code.extraData(Zir.Inst.MultiOp, inst_data.payload_index);
8661 const args = sema.code.refSlice(extra.end, extra.data.operands_len);8695 const args = sema.code.refSlice(extra.end, extra.data.operands_len);
8696 assert(args.len != 0);
8697
8698 const resolved_args = try gpa.alloc(Air.Inst.Ref, args.len);
8699 defer gpa.free(resolved_args);
86628700
8663 var resolved_args = try sema.mod.gpa.alloc(Air.Inst.Ref, args.len);
8664 for (args) |arg, i| resolved_args[i] = sema.resolveInst(arg);8701 for (args) |arg, i| resolved_args[i] = sema.resolveInst(arg);
86658702
8666 var all_args_comptime = for (resolved_args) |arg| {8703 const elem_ty = sema.typeOf(resolved_args[0]);
8667 if ((try sema.resolveMaybeUndefVal(block, src, arg)) == null) break false;8704
8668 } else true;8705 const array_ty = try Type.Tag.array.create(sema.arena, .{
8706 .len = resolved_args.len,
8707 .elem_type = elem_ty,
8708 });
8709
8710 const opt_runtime_src: ?LazySrcLoc = for (resolved_args) |arg| {
8711 const arg_src = src; // TODO better source location
8712 const comptime_known = try sema.isComptimeKnown(block, arg_src, arg);
8713 if (!comptime_known) break arg_src;
8714 } else null;
86698715
8670 if (all_args_comptime) {8716 const runtime_src = opt_runtime_src orelse {
8671 var anon_decl = try block.startAnonDecl();8717 var anon_decl = try block.startAnonDecl();
8672 defer anon_decl.deinit();8718 defer anon_decl.deinit();
8673 assert(!(resolved_args.len == 0));8719
8674 const final_ty = try Type.Tag.array.create(anon_decl.arena(), .{8720 const elem_vals = try anon_decl.arena().alloc(Value, resolved_args.len);
8675 .len = resolved_args.len,
8676 .elem_type = try sema.typeOf(resolved_args[0]).copy(anon_decl.arena()),
8677 });
8678 const buf = try anon_decl.arena().alloc(Value, resolved_args.len);
8679 for (resolved_args) |arg, i| {8721 for (resolved_args) |arg, i| {
8680 buf[i] = try (try sema.resolveMaybeUndefVal(block, src, arg)).?.copy(anon_decl.arena());8722 // We checked that all args are comptime above.
8723 const arg_val = (sema.resolveMaybeUndefVal(block, src, arg) catch unreachable).?;
8724 elem_vals[i] = try arg_val.copy(anon_decl.arena());
8681 }8725 }
86828726
8683 const val = try Value.Tag.array.create(anon_decl.arena(), buf);8727 const val = try Value.Tag.array.create(anon_decl.arena(), elem_vals);
8684 if (is_ref)8728 const decl = try anon_decl.finish(try array_ty.copy(anon_decl.arena()), val);
8685 return sema.analyzeDeclRef(try anon_decl.finish(final_ty, val))8729 if (is_ref) {
8686 else8730 return sema.analyzeDeclRef(decl);
8687 return sema.analyzeDeclVal(block, .unneeded, try anon_decl.finish(final_ty, val));8731 } else {
8688 }8732 return sema.analyzeDeclVal(block, .unneeded, decl);
8733 }
8734 };
86898735
8690 assert(!(resolved_args.len == 0));8736 try sema.requireRuntimeBlock(block, runtime_src);
8691 const array_ty = try Type.Tag.array.create(sema.arena, .{ .len = resolved_args.len, .elem_type = sema.typeOf(resolved_args[0]) });8737
8692 const final_ty = try Type.ptr(sema.arena, .{8738 const alloc_ty = try Type.ptr(sema.arena, .{
8693 .pointee_type = array_ty,8739 .pointee_type = array_ty,
8694 .@"addrspace" = target_util.defaultAddressSpace(sema.mod.getTarget(), .local),8740 .@"addrspace" = target_util.defaultAddressSpace(sema.mod.getTarget(), .local),
8695 });8741 });
8696 const alloc = try block.addTy(.alloc, final_ty);8742 const alloc = try block.addTy(.alloc, alloc_ty);
86978743
8698 for (resolved_args) |arg, i| {8744 for (resolved_args) |arg, i| {
8699 const pointer_to_array_at_index = try block.addBinOp(.ptr_elem_ptr, alloc, try sema.addIntUnsigned(Type.initTag(.u64), i));8745 const index = try sema.addIntUnsigned(Type.initTag(.u64), i);
8700 _ = try block.addBinOp(.store, pointer_to_array_at_index, arg);8746 const elem_ptr = try block.addBinOp(.ptr_elem_ptr, alloc, index);
8747 _ = try block.addBinOp(.store, elem_ptr, arg);
8748 }
8749 if (is_ref) {
8750 return alloc;
8751 } else {
8752 return sema.analyzeLoad(block, .unneeded, alloc, .unneeded);
8701 }8753 }
8702 return if (is_ref)
8703 alloc
8704 else
8705 try sema.analyzeLoad(block, .unneeded, alloc, .unneeded);
8706}8754}
87078755
8708fn zirArrayInitAnon(sema: *Sema, block: *Block, inst: Zir.Inst.Index, is_ref: bool) CompileError!Air.Inst.Ref {8756fn zirArrayInitAnon(sema: *Sema, block: *Block, inst: Zir.Inst.Index, is_ref: bool) CompileError!Air.Inst.Ref {
...@@ -10111,7 +10159,8 @@ fn panicWithMsg(...@@ -10111,7 +10159,8 @@ fn panicWithMsg(
10111 const arena = sema.arena;10159 const arena = sema.arena;
1011210160
10113 const this_feature_is_implemented_in_the_backend =10161 const this_feature_is_implemented_in_the_backend =
10114 mod.comp.bin_file.options.object_format == .c;10162 mod.comp.bin_file.options.object_format == .c or
10163 mod.comp.bin_file.options.use_llvm;
10115 if (!this_feature_is_implemented_in_the_backend) {10164 if (!this_feature_is_implemented_in_the_backend) {
10116 // TODO implement this feature in all the backends and then delete this branch10165 // TODO implement this feature in all the backends and then delete this branch
10117 _ = try block.addNoOp(.breakpoint);10166 _ = try block.addNoOp(.breakpoint);
...@@ -10579,8 +10628,9 @@ fn fieldCallBind(...@@ -10579,8 +10628,9 @@ fn fieldCallBind(
10579 const struct_ty = try sema.resolveTypeFields(block, src, concrete_ty);10628 const struct_ty = try sema.resolveTypeFields(block, src, concrete_ty);
10580 const struct_obj = struct_ty.castTag(.@"struct").?.data;10629 const struct_obj = struct_ty.castTag(.@"struct").?.data;
1058110630
10582 const field_index = struct_obj.fields.getIndex(field_name) orelse10631 const field_index_usize = struct_obj.fields.getIndex(field_name) orelse
10583 break :find_field;10632 break :find_field;
10633 const field_index = @intCast(u32, field_index_usize);
10584 const field = struct_obj.fields.values()[field_index];10634 const field = struct_obj.fields.values()[field_index];
1058510635
10586 const ptr_field_ty = try Type.ptr(arena, .{10636 const ptr_field_ty = try Type.ptr(arena, .{
...@@ -10601,33 +10651,7 @@ fn fieldCallBind(...@@ -10601,33 +10651,7 @@ fn fieldCallBind(
10601 }10651 }
1060210652
10603 try sema.requireRuntimeBlock(block, src);10653 try sema.requireRuntimeBlock(block, src);
10604 const ptr_inst = ptr_inst: {10654 const ptr_inst = try block.addStructFieldPtr(object_ptr, field_index, ptr_field_ty);
10605 const tag: Air.Inst.Tag = switch (field_index) {
10606 0 => .struct_field_ptr_index_0,
10607 1 => .struct_field_ptr_index_1,
10608 2 => .struct_field_ptr_index_2,
10609 3 => .struct_field_ptr_index_3,
10610 else => {
10611 break :ptr_inst try block.addInst(.{
10612 .tag = .struct_field_ptr,
10613 .data = .{ .ty_pl = .{
10614 .ty = try sema.addType(ptr_field_ty),
10615 .payload = try sema.addExtra(Air.StructField{
10616 .struct_operand = object_ptr,
10617 .field_index = @intCast(u32, field_index),
10618 }),
10619 } },
10620 });
10621 },
10622 };
10623 break :ptr_inst try block.addInst(.{
10624 .tag = tag,
10625 .data = .{ .ty_op = .{
10626 .ty = try sema.addType(ptr_field_ty),
10627 .operand = object_ptr,
10628 } },
10629 });
10630 };
10631 return sema.analyzeLoad(block, src, ptr_inst, src);10655 return sema.analyzeLoad(block, src, ptr_inst, src);
10632 },10656 },
10633 .Union => return sema.fail(block, src, "TODO implement field calls on unions", .{}),10657 .Union => return sema.fail(block, src, "TODO implement field calls on unions", .{}),
...@@ -10982,10 +11006,24 @@ fn elemVal(...@@ -10982,10 +11006,24 @@ fn elemVal(
10982 }11006 }
10983 },11007 },
10984 },11008 },
11009 .Array => {
11010 if (try sema.resolveMaybeUndefVal(block, src, array_maybe_ptr)) |array_val| {
11011 const elem_ty = maybe_ptr_ty.childType();
11012 const opt_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index);
11013 if (array_val.isUndef()) return sema.addConstUndef(elem_ty);
11014 if (opt_index_val) |index_val| {
11015 const index = @intCast(usize, index_val.toUnsignedInt());
11016 const elem_val = try array_val.elemValue(sema.arena, index);
11017 return sema.addConstant(elem_ty, elem_val);
11018 }
11019 }
11020 try sema.requireRuntimeBlock(block, src);
11021 return block.addBinOp(.array_elem_val, array_maybe_ptr, elem_index);
11022 },
10985 else => return sema.fail(11023 else => return sema.fail(
10986 block,11024 block,
10987 array_ptr_src,11025 array_ptr_src,
10988 "expected pointer, found '{}'",11026 "expected pointer or array; found '{}'",
10989 .{maybe_ptr_ty},11027 .{maybe_ptr_ty},
10990 ),11028 ),
10991 }11029 }
...@@ -11085,6 +11123,14 @@ fn coerce(...@@ -11085,6 +11123,14 @@ fn coerce(
11085 return sema.wrapOptional(block, dest_type, intermediate, inst_src);11123 return sema.wrapOptional(block, dest_type, intermediate, inst_src);
11086 },11124 },
11087 .Pointer => {11125 .Pointer => {
11126 // Function body to function pointer.
11127 if (inst_ty.zigTypeTag() == .Fn) {
11128 const fn_val = try sema.resolveConstValue(block, inst_src, inst);
11129 const fn_decl = fn_val.castTag(.function).?.data.owner_decl;
11130 const inst_as_ptr = try sema.analyzeDeclRef(fn_decl);
11131 return sema.coerce(block, dest_type, inst_as_ptr, inst_src);
11132 }
11133
11088 // Coercions where the source is a single pointer to an array.11134 // Coercions where the source is a single pointer to an array.
11089 src_array_ptr: {11135 src_array_ptr: {
11090 if (!inst_ty.isSinglePointer()) break :src_array_ptr;11136 if (!inst_ty.isSinglePointer()) break :src_array_ptr;
...@@ -11411,7 +11457,7 @@ fn storePtr2(...@@ -11411,7 +11457,7 @@ fn storePtr2(
11411 if (ptr_ty.isConstPtr())11457 if (ptr_ty.isConstPtr())
11412 return sema.fail(block, src, "cannot assign to constant", .{});11458 return sema.fail(block, src, "cannot assign to constant", .{});
1141311459
11414 const elem_ty = ptr_ty.elemType();11460 const elem_ty = ptr_ty.childType();
11415 const operand = try sema.coerce(block, elem_ty, uncasted_operand, operand_src);11461 const operand = try sema.coerce(block, elem_ty, uncasted_operand, operand_src);
11416 if ((try sema.typeHasOnePossibleValue(block, src, elem_ty)) != null)11462 if ((try sema.typeHasOnePossibleValue(block, src, elem_ty)) != null)
11417 return;11463 return;
...@@ -11429,6 +11475,7 @@ fn storePtr2(...@@ -11429,6 +11475,7 @@ fn storePtr2(
11429 // TODO handle if the element type requires comptime11475 // TODO handle if the element type requires comptime
1143011476
11431 try sema.requireRuntimeBlock(block, runtime_src);11477 try sema.requireRuntimeBlock(block, runtime_src);
11478 try sema.resolveTypeLayout(block, src, elem_ty);
11432 _ = try block.addBinOp(air_tag, ptr, operand);11479 _ = try block.addBinOp(air_tag, ptr, operand);
11433}11480}
1143411481
src/arch/x86_64/abi.zig created+337
...@@ -0,0 +1,337 @@
1const std = @import("std");
2const Type = @import("../../type.zig").Type;
3const Target = std.Target;
4const assert = std.debug.assert;
5
6pub const Class = enum { integer, sse, sseup, x87, x87up, complex_x87, memory, none };
7
8pub fn classifyWindows(ty: Type, target: Target) Class {
9 // https://docs.microsoft.com/en-gb/cpp/build/x64-calling-convention?view=vs-2017
10 // "There's a strict one-to-one correspondence between a function call's arguments
11 // and the registers used for those arguments. Any argument that doesn't fit in 8
12 // bytes, or isn't 1, 2, 4, or 8 bytes, must be passed by reference. A single argument
13 // is never spread across multiple registers."
14 // "Structs and unions of size 8, 16, 32, or 64 bits, and __m64 types, are passed
15 // as if they were integers of the same size."
16 switch (ty.abiSize(target)) {
17 1, 2, 4, 8 => {},
18 else => return .memory,
19 }
20 return switch (ty.zigTypeTag()) {
21 .Int, .Bool, .Enum, .Void, .NoReturn, .ErrorSet, .Struct, .Union => .integer,
22 .Optional => if (ty.isPtrLikeOptional()) return .integer else return .memory,
23 .Float, .Vector => .sse,
24 else => unreachable,
25 };
26}
27
28/// There are a maximum of 8 possible return slots. Returned values are in
29/// the beginning of the array; unused slots are filled with .none.
30pub fn classifySystemV(ty: Type, target: Target) [8]Class {
31 const memory_class = [_]Class{
32 .memory, .none, .none, .none,
33 .none, .none, .none, .none,
34 };
35 var result = [1]Class{.none} ** 8;
36 switch (ty.zigTypeTag()) {
37 .Int, .Enum, .ErrorSet => {
38 const bits = ty.intInfo(target).bits;
39 if (bits <= 64) {
40 result[0] = .integer;
41 return result;
42 }
43 if (bits <= 128) {
44 result[0] = .integer;
45 result[1] = .integer;
46 return result;
47 }
48 if (bits <= 192) {
49 result[0] = .integer;
50 result[1] = .integer;
51 result[2] = .integer;
52 return result;
53 }
54 if (bits <= 256) {
55 result[0] = .integer;
56 result[1] = .integer;
57 result[2] = .integer;
58 result[3] = .integer;
59 return result;
60 }
61 return memory_class;
62 },
63 .Bool, .Void, .NoReturn => {
64 result[0] = .integer;
65 return result;
66 },
67 .Float => switch (ty.floatBits(target)) {
68 16, 32, 64 => {
69 result[0] = .sse;
70 return result;
71 },
72 128 => {
73 // "Arguments of types__float128,_Decimal128and__m128are
74 // split into two halves. The least significant ones belong
75 // to class SSE, the mostsignificant one to class SSEUP."
76 result[0] = .sse;
77 result[1] = .sseup;
78 return result;
79 },
80 else => {
81 // "The 64-bit mantissa of arguments of typelong double
82 // belongs to classX87, the 16-bit exponent plus 6 bytes
83 // of padding belongs to class X87UP."
84 result[0] = .x87;
85 result[1] = .x87up;
86 return result;
87 },
88 },
89 .Vector => {
90 const elem_ty = ty.childType();
91 const bits = elem_ty.bitSize(target) * ty.arrayLen();
92 if (bits <= 64) return .{
93 .sse, .none, .none, .none,
94 .none, .none, .none, .none,
95 };
96 if (bits <= 128) return .{
97 .sse, .sseup, .none, .none,
98 .none, .none, .none, .none,
99 };
100 if (bits <= 192) return .{
101 .sse, .sseup, .sseup, .none,
102 .none, .none, .none, .none,
103 };
104 if (bits <= 256) return .{
105 .sse, .sseup, .sseup, .sseup,
106 .none, .none, .none, .none,
107 };
108 if (bits <= 320) return .{
109 .sse, .sseup, .sseup, .sseup,
110 .sseup, .none, .none, .none,
111 };
112 if (bits <= 384) return .{
113 .sse, .sseup, .sseup, .sseup,
114 .sseup, .sseup, .none, .none,
115 };
116 if (bits <= 448) return .{
117 .sse, .sseup, .sseup, .sseup,
118 .sseup, .sseup, .sseup, .none,
119 };
120 if (bits <= 512) return .{
121 .sse, .sseup, .sseup, .sseup,
122 .sseup, .sseup, .sseup, .sseup,
123 };
124 return memory_class;
125 },
126 .Optional => {
127 if (ty.isPtrLikeOptional()) {
128 result[0] = .integer;
129 return result;
130 }
131 return memory_class;
132 },
133 .Struct => {
134 // "If the size of an object is larger than eight eightbytes, or
135 // it contains unaligned fields, it has class MEMORY"
136 // "If the size of the aggregate exceeds a single eightbyte, each is classified
137 // separately.".
138 const ty_size = ty.abiSize(target);
139 if (ty_size > 64)
140 return memory_class;
141
142 var result_i: usize = 0; // out of 8
143 var byte_i: usize = 0; // out of 8
144 const fields = ty.structFields();
145 for (fields.values()) |field| {
146 if (field.abi_align.tag() != .abi_align_default) {
147 const field_alignment = field.abi_align.toUnsignedInt();
148 if (field_alignment < field.ty.abiAlignment(target)) {
149 return memory_class;
150 }
151 }
152 const field_size = field.ty.abiSize(target);
153 const field_class_array = classifySystemV(field.ty, target);
154 const field_class = std.mem.sliceTo(&field_class_array, .none);
155 if (byte_i + field_size <= 8) {
156 // Combine this field with the previous one.
157 combine: {
158 // "If both classes are equal, this is the resulting class."
159 if (result[result_i] == field_class[0]) {
160 break :combine;
161 }
162
163 // "If one of the classes is NO_CLASS, the resulting class
164 // is the other class."
165 if (result[result_i] == .none) {
166 result[result_i] = field_class[0];
167 break :combine;
168 }
169 assert(field_class[0] != .none);
170
171 // "If one of the classes is MEMORY, the result is the MEMORY class."
172 if (result[result_i] == .memory or field_class[0] == .memory) {
173 result[result_i] = .memory;
174 break :combine;
175 }
176
177 // "If one of the classes is INTEGER, the result is the INTEGER."
178 if (result[result_i] == .integer or field_class[0] == .integer) {
179 result[result_i] = .integer;
180 break :combine;
181 }
182
183 // "If one of the classes is X87, X87UP, COMPLEX_X87 class,
184 // MEMORY is used as class."
185 if (result[result_i] == .x87 or
186 result[result_i] == .x87up or
187 result[result_i] == .complex_x87 or
188 field_class[0] == .x87 or
189 field_class[0] == .x87up or
190 field_class[0] == .complex_x87)
191 {
192 result[result_i] = .memory;
193 break :combine;
194 }
195
196 // "Otherwise class SSE is used."
197 result[result_i] = .sse;
198 }
199 byte_i += field_size;
200 if (byte_i == 8) {
201 byte_i = 0;
202 result_i += 1;
203 }
204 } else {
205 // Cannot combine this field with the previous one.
206 if (byte_i != 0) {
207 byte_i = 0;
208 result_i += 1;
209 }
210 std.mem.copy(Class, result[result_i..], field_class);
211 result_i += field_class.len;
212 // If there are any bytes leftover, we have to try to combine
213 // the next field with them.
214 byte_i = field_size % 8;
215 if (byte_i != 0) result_i -= 1;
216 }
217 }
218
219 // Post-merger cleanup
220
221 // "If one of the classes is MEMORY, the whole argument is passed in memory"
222 // "If X87UP is not preceded by X87, the whole argument is passed in memory."
223 var found_sseup = false;
224 for (result) |item, i| switch (item) {
225 .memory => return memory_class,
226 .x87up => if (i == 0 or result[i - 1] != .x87) return memory_class,
227 .sseup => found_sseup = true,
228 else => continue,
229 };
230 // "If the size of the aggregate exceeds two eightbytes and the first eight-
231 // byte isn’t SSE or any other eightbyte isn’t SSEUP, the whole argument
232 // is passed in memory."
233 if (ty_size > 16 and (result[0] != .sse or !found_sseup)) return memory_class;
234
235 // "If SSEUP is not preceded by SSE or SSEUP, it is converted to SSE."
236 for (result) |*item, i| {
237 if (item.* == .sseup) switch (result[i - 1]) {
238 .sse, .sseup => continue,
239 else => item.* = .sse,
240 };
241 }
242 return result;
243 },
244 .Union => {
245 // "If the size of an object is larger than eight eightbytes, or
246 // it contains unaligned fields, it has class MEMORY"
247 // "If the size of the aggregate exceeds a single eightbyte, each is classified
248 // separately.".
249 const ty_size = ty.abiSize(target);
250 if (ty_size > 64)
251 return memory_class;
252
253 const fields = ty.unionFields();
254 for (fields.values()) |field| {
255 if (field.abi_align.tag() != .abi_align_default) {
256 const field_alignment = field.abi_align.toUnsignedInt();
257 if (field_alignment < field.ty.abiAlignment(target)) {
258 return memory_class;
259 }
260 }
261 // Combine this field with the previous one.
262 const field_class = classifySystemV(field.ty, target);
263 for (result) |*result_item, i| {
264 const field_item = field_class[i];
265 // "If both classes are equal, this is the resulting class."
266 if (result_item.* == field_item) {
267 continue;
268 }
269
270 // "If one of the classes is NO_CLASS, the resulting class
271 // is the other class."
272 if (result_item.* == .none) {
273 result_item.* = field_item;
274 continue;
275 }
276 if (field_item == .none) {
277 continue;
278 }
279
280 // "If one of the classes is MEMORY, the result is the MEMORY class."
281 if (result_item.* == .memory or field_item == .memory) {
282 result_item.* = .memory;
283 continue;
284 }
285
286 // "If one of the classes is INTEGER, the result is the INTEGER."
287 if (result_item.* == .integer or field_item == .integer) {
288 result_item.* = .integer;
289 continue;
290 }
291
292 // "If one of the classes is X87, X87UP, COMPLEX_X87 class,
293 // MEMORY is used as class."
294 if (result_item.* == .x87 or
295 result_item.* == .x87up or
296 result_item.* == .complex_x87 or
297 field_item == .x87 or
298 field_item == .x87up or
299 field_item == .complex_x87)
300 {
301 result_item.* = .memory;
302 continue;
303 }
304
305 // "Otherwise class SSE is used."
306 result_item.* = .sse;
307 }
308 }
309
310 // Post-merger cleanup
311
312 // "If one of the classes is MEMORY, the whole argument is passed in memory"
313 // "If X87UP is not preceded by X87, the whole argument is passed in memory."
314 var found_sseup = false;
315 for (result) |item, i| switch (item) {
316 .memory => return memory_class,
317 .x87up => if (i == 0 or result[i - 1] != .x87) return memory_class,
318 .sseup => found_sseup = true,
319 else => continue,
320 };
321 // "If the size of the aggregate exceeds two eightbytes and the first eight-
322 // byte isn’t SSE or any other eightbyte isn’t SSEUP, the whole argument
323 // is passed in memory."
324 if (ty_size > 16 and (result[0] != .sse or !found_sseup)) return memory_class;
325
326 // "If SSEUP is not preceded by SSE or SSEUP, it is converted to SSE."
327 for (result) |*item, i| {
328 if (item.* == .sseup) switch (result[i - 1]) {
329 .sse, .sseup => continue,
330 else => item.* = .sse,
331 };
332 }
333 return result;
334 },
335 else => unreachable,
336 }
337}
src/codegen.zig+24
...@@ -855,6 +855,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -855,6 +855,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
855 .shr => try self.airShr(inst),855 .shr => try self.airShr(inst),
856856
857 .alloc => try self.airAlloc(inst),857 .alloc => try self.airAlloc(inst),
858 .ret_ptr => try self.airRetPtr(inst),
858 .arg => try self.airArg(inst),859 .arg => try self.airArg(inst),
859 .assembly => try self.airAsm(inst),860 .assembly => try self.airAsm(inst),
860 .bitcast => try self.airBitCast(inst),861 .bitcast => try self.airBitCast(inst),
...@@ -883,6 +884,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -883,6 +884,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
883 .not => try self.airNot(inst),884 .not => try self.airNot(inst),
884 .ptrtoint => try self.airPtrToInt(inst),885 .ptrtoint => try self.airPtrToInt(inst),
885 .ret => try self.airRet(inst),886 .ret => try self.airRet(inst),
887 .ret_load => try self.airRetLoad(inst),
886 .store => try self.airStore(inst),888 .store => try self.airStore(inst),
887 .struct_field_ptr=> try self.airStructFieldPtr(inst),889 .struct_field_ptr=> try self.airStructFieldPtr(inst),
888 .struct_field_val=> try self.airStructFieldVal(inst),890 .struct_field_val=> try self.airStructFieldVal(inst),
...@@ -914,6 +916,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -914,6 +916,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
914 .slice_ptr => try self.airSlicePtr(inst),916 .slice_ptr => try self.airSlicePtr(inst),
915 .slice_len => try self.airSliceLen(inst),917 .slice_len => try self.airSliceLen(inst),
916918
919 .array_elem_val => try self.airArrayElemVal(inst),
917 .slice_elem_val => try self.airSliceElemVal(inst),920 .slice_elem_val => try self.airSliceElemVal(inst),
918 .ptr_slice_elem_val => try self.airPtrSliceElemVal(inst),921 .ptr_slice_elem_val => try self.airPtrSliceElemVal(inst),
919 .ptr_elem_val => try self.airPtrElemVal(inst),922 .ptr_elem_val => try self.airPtrElemVal(inst),
...@@ -1185,6 +1188,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1185,6 +1188,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1185 return self.finishAir(inst, .{ .ptr_stack_offset = stack_offset }, .{ .none, .none, .none });1188 return self.finishAir(inst, .{ .ptr_stack_offset = stack_offset }, .{ .none, .none, .none });
1186 }1189 }
11871190
1191 fn airRetPtr(self: *Self, inst: Air.Inst.Index) !void {
1192 const stack_offset = try self.allocMemPtr(inst);
1193 return self.finishAir(inst, .{ .ptr_stack_offset = stack_offset }, .{ .none, .none, .none });
1194 }
1195
1188 fn airFptrunc(self: *Self, inst: Air.Inst.Index) !void {1196 fn airFptrunc(self: *Self, inst: Air.Inst.Index) !void {
1189 const ty_op = self.air.instructions.items(.data)[inst].ty_op;1197 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1190 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {1198 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
...@@ -1557,6 +1565,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1557,6 +1565,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1557 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });1565 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1558 }1566 }
15591567
1568 fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void {
1569 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1570 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
1571 else => return self.fail("TODO implement array_elem_val for {}", .{self.target.cpu.arch}),
1572 };
1573 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1574 }
1575
1560 fn airPtrSliceElemVal(self: *Self, inst: Air.Inst.Index) !void {1576 fn airPtrSliceElemVal(self: *Self, inst: Air.Inst.Index) !void {
1561 const is_volatile = false; // TODO1577 const is_volatile = false; // TODO
1562 const bin_op = self.air.instructions.items(.data)[inst].bin_op;1578 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
...@@ -3213,6 +3229,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3213,6 +3229,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3213 return self.finishAir(inst, .dead, .{ un_op, .none, .none });3229 return self.finishAir(inst, .dead, .{ un_op, .none, .none });
3214 }3230 }
32153231
3232 fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void {
3233 const un_op = self.air.instructions.items(.data)[inst].un_op;
3234 const ptr = try self.resolveInst(un_op);
3235 _ = ptr;
3236 return self.fail("TODO implement airRetLoad for {}", .{self.target.cpu.arch});
3237 //return self.finishAir(inst, .dead, .{ un_op, .none, .none });
3238 }
3239
3216 fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void {3240 fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void {
3217 const bin_op = self.air.instructions.items(.data)[inst].bin_op;3241 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
3218 if (self.liveness.isUnused(inst))3242 if (self.liveness.isUnused(inst))
src/codegen/c.zig+67-17
...@@ -384,12 +384,6 @@ pub const DeclGen = struct {...@@ -384,12 +384,6 @@ pub const DeclGen = struct {
384 }384 }
385 },385 },
386 .Fn => switch (val.tag()) {386 .Fn => switch (val.tag()) {
387 .null_value, .zero => try writer.writeAll("NULL"),
388 .one => try writer.writeAll("1"),
389 .decl_ref => {
390 const decl = val.castTag(.decl_ref).?.data;
391 return dg.renderDeclValue(writer, ty, val, decl);
392 },
393 .function => {387 .function => {
394 const decl = val.castTag(.function).?.data.owner_decl;388 const decl = val.castTag(.function).?.data.owner_decl;
395 return dg.renderDeclValue(writer, ty, val, decl);389 return dg.renderDeclValue(writer, ty, val, decl);
...@@ -1026,6 +1020,7 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO...@@ -1026,6 +1020,7 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
1026 .is_non_null_ptr => try airIsNull(f, inst, "!=", "[0]"),1020 .is_non_null_ptr => try airIsNull(f, inst, "!=", "[0]"),
10271021
1028 .alloc => try airAlloc(f, inst),1022 .alloc => try airAlloc(f, inst),
1023 .ret_ptr => try airRetPtr(f, inst),
1029 .assembly => try airAsm(f, inst),1024 .assembly => try airAsm(f, inst),
1030 .block => try airBlock(f, inst),1025 .block => try airBlock(f, inst),
1031 .bitcast => try airBitcast(f, inst),1026 .bitcast => try airBitcast(f, inst),
...@@ -1036,6 +1031,7 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO...@@ -1036,6 +1031,7 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
1036 .bool_to_int => try airBoolToInt(f, inst),1031 .bool_to_int => try airBoolToInt(f, inst),
1037 .load => try airLoad(f, inst),1032 .load => try airLoad(f, inst),
1038 .ret => try airRet(f, inst),1033 .ret => try airRet(f, inst),
1034 .ret_load => try airRetLoad(f, inst),
1039 .store => try airStore(f, inst),1035 .store => try airStore(f, inst),
1040 .loop => try airLoop(f, inst),1036 .loop => try airLoop(f, inst),
1041 .cond_br => try airCondBr(f, inst),1037 .cond_br => try airCondBr(f, inst),
...@@ -1081,6 +1077,7 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO...@@ -1081,6 +1077,7 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
1081 .ptr_elem_ptr => try airPtrElemPtr(f, inst),1077 .ptr_elem_ptr => try airPtrElemPtr(f, inst),
1082 .slice_elem_val => try airSliceElemVal(f, inst, "["),1078 .slice_elem_val => try airSliceElemVal(f, inst, "["),
1083 .ptr_slice_elem_val => try airSliceElemVal(f, inst, "[0]["),1079 .ptr_slice_elem_val => try airSliceElemVal(f, inst, "[0]["),
1080 .array_elem_val => try airArrayElemVal(f, inst),
10841081
1085 .unwrap_errunion_payload => try airUnwrapErrUnionPay(f, inst),1082 .unwrap_errunion_payload => try airUnwrapErrUnionPay(f, inst),
1086 .unwrap_errunion_err => try airUnwrapErrUnionErr(f, inst),1083 .unwrap_errunion_err => try airUnwrapErrUnionErr(f, inst),
...@@ -1148,6 +1145,22 @@ fn airSliceElemVal(f: *Function, inst: Air.Inst.Index, prefix: []const u8) !CVal...@@ -1148,6 +1145,22 @@ fn airSliceElemVal(f: *Function, inst: Air.Inst.Index, prefix: []const u8) !CVal
1148 return local;1145 return local;
1149}1146}
11501147
1148fn airArrayElemVal(f: *Function, inst: Air.Inst.Index) !CValue {
1149 if (f.liveness.isUnused(inst)) return CValue.none;
1150
1151 const bin_op = f.air.instructions.items(.data)[inst].bin_op;
1152 const array = try f.resolveInst(bin_op.lhs);
1153 const index = try f.resolveInst(bin_op.rhs);
1154 const writer = f.object.writer();
1155 const local = try f.allocLocal(f.air.typeOfIndex(inst), .Const);
1156 try writer.writeAll(" = ");
1157 try f.writeCValue(writer, array);
1158 try writer.writeAll("[");
1159 try f.writeCValue(writer, index);
1160 try writer.writeAll("];\n");
1161 return local;
1162}
1163
1151fn airAlloc(f: *Function, inst: Air.Inst.Index) !CValue {1164fn airAlloc(f: *Function, inst: Air.Inst.Index) !CValue {
1152 const writer = f.object.writer();1165 const writer = f.object.writer();
1153 const inst_ty = f.air.typeOfIndex(inst);1166 const inst_ty = f.air.typeOfIndex(inst);
...@@ -1161,6 +1174,18 @@ fn airAlloc(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -1161,6 +1174,18 @@ fn airAlloc(f: *Function, inst: Air.Inst.Index) !CValue {
1161 return CValue{ .local_ref = local.local };1174 return CValue{ .local_ref = local.local };
1162}1175}
11631176
1177fn airRetPtr(f: *Function, inst: Air.Inst.Index) !CValue {
1178 const writer = f.object.writer();
1179 const inst_ty = f.air.typeOfIndex(inst);
1180
1181 // First line: the variable used as data storage.
1182 const elem_type = inst_ty.elemType();
1183 const local = try f.allocLocal(elem_type, .Mut);
1184 try writer.writeAll(";\n");
1185
1186 return CValue{ .local_ref = local.local };
1187}
1188
1164fn airArg(f: *Function) CValue {1189fn airArg(f: *Function) CValue {
1165 const i = f.next_arg_index;1190 const i = f.next_arg_index;
1166 f.next_arg_index += 1;1191 f.next_arg_index += 1;
...@@ -1212,6 +1237,21 @@ fn airRet(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -1212,6 +1237,21 @@ fn airRet(f: *Function, inst: Air.Inst.Index) !CValue {
1212 return CValue.none;1237 return CValue.none;
1213}1238}
12141239
1240fn airRetLoad(f: *Function, inst: Air.Inst.Index) !CValue {
1241 const un_op = f.air.instructions.items(.data)[inst].un_op;
1242 const writer = f.object.writer();
1243 const ptr_ty = f.air.typeOf(un_op);
1244 const ret_ty = ptr_ty.childType();
1245 if (!ret_ty.hasCodeGenBits()) {
1246 try writer.writeAll("return;\n");
1247 }
1248 const ptr = try f.resolveInst(un_op);
1249 try writer.writeAll("return *");
1250 try f.writeCValue(writer, ptr);
1251 try writer.writeAll(";\n");
1252 return CValue.none;
1253}
1254
1215fn airIntCast(f: *Function, inst: Air.Inst.Index) !CValue {1255fn airIntCast(f: *Function, inst: Air.Inst.Index) !CValue {
1216 if (f.liveness.isUnused(inst))1256 if (f.liveness.isUnused(inst))
1217 return CValue.none;1257 return CValue.none;
...@@ -1559,7 +1599,12 @@ fn airCall(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -1559,7 +1599,12 @@ fn airCall(f: *Function, inst: Air.Inst.Index) !CValue {
1559 const pl_op = f.air.instructions.items(.data)[inst].pl_op;1599 const pl_op = f.air.instructions.items(.data)[inst].pl_op;
1560 const extra = f.air.extraData(Air.Call, pl_op.payload);1600 const extra = f.air.extraData(Air.Call, pl_op.payload);
1561 const args = @bitCast([]const Air.Inst.Ref, f.air.extra[extra.end..][0..extra.data.args_len]);1601 const args = @bitCast([]const Air.Inst.Ref, f.air.extra[extra.end..][0..extra.data.args_len]);
1562 const fn_ty = f.air.typeOf(pl_op.operand);1602 const callee_ty = f.air.typeOf(pl_op.operand);
1603 const fn_ty = switch (callee_ty.zigTypeTag()) {
1604 .Fn => callee_ty,
1605 .Pointer => callee_ty.childType(),
1606 else => unreachable,
1607 };
1563 const ret_ty = fn_ty.fnReturnType();1608 const ret_ty = fn_ty.fnReturnType();
1564 const unused_result = f.liveness.isUnused(inst);1609 const unused_result = f.liveness.isUnused(inst);
1565 const writer = f.object.writer();1610 const writer = f.object.writer();
...@@ -1574,16 +1619,21 @@ fn airCall(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -1574,16 +1619,21 @@ fn airCall(f: *Function, inst: Air.Inst.Index) !CValue {
1574 try writer.writeAll(" = ");1619 try writer.writeAll(" = ");
1575 }1620 }
15761621
1577 if (f.air.value(pl_op.operand)) |func_val| {1622 callee: {
1578 const fn_decl = if (func_val.castTag(.extern_fn)) |extern_fn|1623 known: {
1579 extern_fn.data1624 const fn_decl = fn_decl: {
1580 else if (func_val.castTag(.function)) |func_payload|1625 const callee_val = f.air.value(pl_op.operand) orelse break :known;
1581 func_payload.data.owner_decl1626 break :fn_decl switch (callee_val.tag()) {
1582 else1627 .extern_fn => callee_val.castTag(.extern_fn).?.data,
1583 unreachable;1628 .function => callee_val.castTag(.function).?.data.owner_decl,
15841629 .decl_ref => callee_val.castTag(.decl_ref).?.data,
1585 try f.object.dg.renderDeclName(fn_decl, writer);1630 else => break :known,
1586 } else {1631 };
1632 };
1633 try f.object.dg.renderDeclName(fn_decl, writer);
1634 break :callee;
1635 }
1636 // Fall back to function pointer call.
1587 const callee = try f.resolveInst(pl_op.operand);1637 const callee = try f.resolveInst(pl_op.operand);
1588 try f.writeCValue(writer, callee);1638 try f.writeCValue(writer, callee);
1589 }1639 }
src/codegen/llvm.zig+427-195
...@@ -21,6 +21,8 @@ const Type = @import("../type.zig").Type;...@@ -21,6 +21,8 @@ const Type = @import("../type.zig").Type;
2121
22const LazySrcLoc = Module.LazySrcLoc;22const LazySrcLoc = Module.LazySrcLoc;
2323
24const Error = error{ OutOfMemory, CodegenFail };
25
24pub fn targetTriple(allocator: *Allocator, target: std.Target) ![:0]u8 {26pub fn targetTriple(allocator: *Allocator, target: std.Target) ![:0]u8 {
25 const llvm_arch = switch (target.cpu.arch) {27 const llvm_arch = switch (target.cpu.arch) {
26 .arm => "arm",28 .arm => "arm",
...@@ -410,10 +412,18 @@ pub const Object = struct {...@@ -410,10 +412,18 @@ pub const Object = struct {
410412
411 // This gets the LLVM values from the function and stores them in `dg.args`.413 // This gets the LLVM values from the function and stores them in `dg.args`.
412 const fn_info = decl.ty.fnInfo();414 const fn_info = decl.ty.fnInfo();
413 var args = try dg.gpa.alloc(*const llvm.Value, fn_info.param_types.len);415 const ret_ty_by_ref = isByRef(fn_info.return_type);
416 const ret_ptr = if (ret_ty_by_ref) llvm_func.getParam(0) else null;
417
418 var args = std.ArrayList(*const llvm.Value).init(dg.gpa);
419 defer args.deinit();
414420
415 for (args) |*arg, i| {421 const param_offset: c_uint = @boolToInt(ret_ptr != null);
416 arg.* = llvm.getParam(llvm_func, @intCast(c_uint, i));422 for (fn_info.param_types) |param_ty| {
423 if (!param_ty.hasCodeGenBits()) continue;
424
425 const llvm_arg_i = @intCast(c_uint, args.items.len) + param_offset;
426 try args.append(llvm_func.getParam(llvm_arg_i));
417 }427 }
418428
419 // Remove all the basic blocks of a function in order to start over, generating429 // Remove all the basic blocks of a function in order to start over, generating
...@@ -434,7 +444,8 @@ pub const Object = struct {...@@ -434,7 +444,8 @@ pub const Object = struct {
434 .context = dg.context,444 .context = dg.context,
435 .dg = &dg,445 .dg = &dg,
436 .builder = builder,446 .builder = builder,
437 .args = args,447 .ret_ptr = ret_ptr,
448 .args = args.toOwnedSlice(),
438 .arg_index = 0,449 .arg_index = 0,
439 .func_inst_table = .{},450 .func_inst_table = .{},
440 .entry_block = entry_block,451 .entry_block = entry_block,
...@@ -556,7 +567,7 @@ pub const DeclGen = struct {...@@ -556,7 +567,7 @@ pub const DeclGen = struct {
556 gpa: *Allocator,567 gpa: *Allocator,
557 err_msg: ?*Module.ErrorMsg,568 err_msg: ?*Module.ErrorMsg,
558569
559 fn todo(self: *DeclGen, comptime format: []const u8, args: anytype) error{ OutOfMemory, CodegenFail } {570 fn todo(self: *DeclGen, comptime format: []const u8, args: anytype) Error {
560 @setCold(true);571 @setCold(true);
561 assert(self.err_msg == null);572 assert(self.err_msg == null);
562 const src_loc = @as(LazySrcLoc, .{ .node_offset = 0 }).toSrcLoc(self.decl);573 const src_loc = @as(LazySrcLoc, .{ .node_offset = 0 }).toSrcLoc(self.decl);
...@@ -591,50 +602,33 @@ pub const DeclGen = struct {...@@ -591,50 +602,33 @@ pub const DeclGen = struct {
591 };602 };
592603
593 const llvm_init = try self.genTypedValue(.{ .ty = decl.ty, .val = init_val });604 const llvm_init = try self.genTypedValue(.{ .ty = decl.ty, .val = init_val });
594 llvm.setInitializer(global, llvm_init);605 global.setInitializer(llvm_init);
595 }606 }
596 }607 }
597608
598 /// If the llvm function does not exist, create it.609 /// If the llvm function does not exist, create it.
599 /// Note that this can be called before the function's semantic analysis has610 /// Note that this can be called before the function's semantic analysis has
600 /// completed, so if any attributes rely on that, they must be done in updateFunc, not here.611 /// completed, so if any attributes rely on that, they must be done in updateFunc, not here.
601 fn resolveLlvmFunction(self: *DeclGen, decl: *Module.Decl) !*const llvm.Value {612 fn resolveLlvmFunction(dg: *DeclGen, decl: *Module.Decl) !*const llvm.Value {
602 const gop = try self.object.decl_map.getOrPut(self.gpa, decl);613 const gop = try dg.object.decl_map.getOrPut(dg.gpa, decl);
603 if (gop.found_existing) return gop.value_ptr.*;614 if (gop.found_existing) return gop.value_ptr.*;
604615
605 assert(decl.has_tv);616 assert(decl.has_tv);
606 const zig_fn_type = decl.ty;617 const zig_fn_type = decl.ty;
607 const fn_info = zig_fn_type.fnInfo();618 const fn_info = zig_fn_type.fnInfo();
608 const return_type = fn_info.return_type;619 const target = dg.module.getTarget();
609620 const sret = firstParamSRet(fn_info, target);
610 const llvm_param_buffer = try self.gpa.alloc(*const llvm.Type, fn_info.param_types.len);
611 defer self.gpa.free(llvm_param_buffer);
612
613 var llvm_params_len: c_uint = 0;
614 for (fn_info.param_types) |param_ty| {
615 if (param_ty.hasCodeGenBits()) {
616 llvm_param_buffer[llvm_params_len] = try self.llvmType(param_ty);
617 llvm_params_len += 1;
618 }
619 }
620621
621 const llvm_ret_ty = if (!return_type.hasCodeGenBits())622 const return_type = fn_info.return_type;
622 self.context.voidType()623 const raw_llvm_ret_ty = try dg.llvmType(return_type);
623 else
624 try self.llvmType(return_type);
625624
626 const fn_type = llvm.functionType(625 const fn_type = try dg.llvmType(zig_fn_type);
627 llvm_ret_ty,
628 llvm_param_buffer.ptr,
629 llvm_params_len,
630 .False,
631 );
632 const llvm_addrspace = self.llvmAddressSpace(decl.@"addrspace");
633626
634 const fqn = try decl.getFullyQualifiedName(self.gpa);627 const fqn = try decl.getFullyQualifiedName(dg.gpa);
635 defer self.gpa.free(fqn);628 defer dg.gpa.free(fqn);
636629
637 const llvm_fn = self.llvmModule().addFunctionInAddressSpace(fqn, fn_type, llvm_addrspace);630 const llvm_addrspace = dg.llvmAddressSpace(decl.@"addrspace");
631 const llvm_fn = dg.llvmModule().addFunctionInAddressSpace(fqn, fn_type, llvm_addrspace);
638 gop.value_ptr.* = llvm_fn;632 gop.value_ptr.* = llvm_fn;
639633
640 const is_extern = decl.val.tag() == .extern_fn;634 const is_extern = decl.val.tag() == .extern_fn;
...@@ -643,53 +637,76 @@ pub const DeclGen = struct {...@@ -643,53 +637,76 @@ pub const DeclGen = struct {
643 llvm_fn.setUnnamedAddr(.True);637 llvm_fn.setUnnamedAddr(.True);
644 }638 }
645639
646 if (self.module.comp.bin_file.options.skip_linker_dependencies) {640 if (sret) {
641 dg.addArgAttr(llvm_fn, 0, "nonnull"); // Sret pointers must not be address 0
642 dg.addArgAttr(llvm_fn, 0, "noalias");
643 llvm_fn.addSretAttr(0, raw_llvm_ret_ty);
644 }
645
646 // Set parameter attributes.
647 var llvm_param_i: c_uint = @boolToInt(sret);
648 for (fn_info.param_types) |param_ty| {
649 if (!param_ty.hasCodeGenBits()) continue;
650
651 if (isByRef(param_ty)) {
652 dg.addArgAttr(llvm_fn, llvm_param_i, "nonnull");
653 // TODO readonly, noalias, align
654 }
655 llvm_param_i += 1;
656 }
657
658 if (dg.module.comp.bin_file.options.skip_linker_dependencies) {
647 // The intent here is for compiler-rt and libc functions to not generate659 // The intent here is for compiler-rt and libc functions to not generate
648 // infinite recursion. For example, if we are compiling the memcpy function,660 // infinite recursion. For example, if we are compiling the memcpy function,
649 // and llvm detects that the body is equivalent to memcpy, it may replace the661 // and llvm detects that the body is equivalent to memcpy, it may replace the
650 // body of memcpy with a call to memcpy, which would then cause a stack662 // body of memcpy with a call to memcpy, which would then cause a stack
651 // overflow instead of performing memcpy.663 // overflow instead of performing memcpy.
652 self.addFnAttr(llvm_fn, "nobuiltin");664 dg.addFnAttr(llvm_fn, "nobuiltin");
653 }665 }
654666
655 // TODO: more attributes. see codegen.cpp `make_fn_llvm_value`.667 // TODO: more attributes. see codegen.cpp `make_fn_llvm_value`.
656 const target = self.module.getTarget();
657 if (fn_info.cc == .Naked) {668 if (fn_info.cc == .Naked) {
658 self.addFnAttr(llvm_fn, "naked");669 dg.addFnAttr(llvm_fn, "naked");
659 } else {670 } else {
660 llvm_fn.setFunctionCallConv(toLlvmCallConv(fn_info.cc, target));671 llvm_fn.setFunctionCallConv(toLlvmCallConv(fn_info.cc, target));
661 }672 }
662673
663 // Function attributes that are independent of analysis results of the function body.674 // Function attributes that are independent of analysis results of the function body.
664 if (!self.module.comp.bin_file.options.red_zone) {675 if (!dg.module.comp.bin_file.options.red_zone) {
665 self.addFnAttr(llvm_fn, "noredzone");676 dg.addFnAttr(llvm_fn, "noredzone");
666 }677 }
667 self.addFnAttr(llvm_fn, "nounwind");678 dg.addFnAttr(llvm_fn, "nounwind");
668 if (self.module.comp.unwind_tables) {679 if (dg.module.comp.unwind_tables) {
669 self.addFnAttr(llvm_fn, "uwtable");680 dg.addFnAttr(llvm_fn, "uwtable");
670 }681 }
671 if (self.module.comp.bin_file.options.optimize_mode == .ReleaseSmall) {682 if (dg.module.comp.bin_file.options.optimize_mode == .ReleaseSmall) {
672 self.addFnAttr(llvm_fn, "minsize");683 dg.addFnAttr(llvm_fn, "minsize");
673 self.addFnAttr(llvm_fn, "optsize");684 dg.addFnAttr(llvm_fn, "optsize");
674 }685 }
675 if (self.module.comp.bin_file.options.tsan) {686 if (dg.module.comp.bin_file.options.tsan) {
676 self.addFnAttr(llvm_fn, "sanitize_thread");687 dg.addFnAttr(llvm_fn, "sanitize_thread");
677 }688 }
678 // TODO add target-cpu and target-features fn attributes689 // TODO add target-cpu and target-features fn attributes
679 if (return_type.isNoReturn()) {690 if (return_type.isNoReturn()) {
680 self.addFnAttr(llvm_fn, "noreturn");691 dg.addFnAttr(llvm_fn, "noreturn");
681 }692 }
682693
683 return llvm_fn;694 return llvm_fn;
684 }695 }
685696
686 fn resolveGlobalDecl(self: *DeclGen, decl: *Module.Decl) error{ OutOfMemory, CodegenFail }!*const llvm.Value {697 fn resolveGlobalDecl(dg: *DeclGen, decl: *Module.Decl) Error!*const llvm.Value {
687 const llvm_module = self.object.llvm_module;698 const gop = try dg.object.decl_map.getOrPut(dg.gpa, decl);
688 if (llvm_module.getNamedGlobal(decl.name)) |val| return val;699 if (gop.found_existing) return gop.value_ptr.*;
689 // TODO: remove this redundant `llvmType`, it is also called in `genTypedValue`.700 errdefer assert(dg.object.decl_map.remove(decl));
690 const llvm_type = try self.llvmType(decl.ty);701
691 const llvm_addrspace = self.llvmAddressSpace(decl.@"addrspace");702 const fqn = try decl.getFullyQualifiedName(dg.gpa);
692 return llvm_module.addGlobalInAddressSpace(llvm_type, decl.name, llvm_addrspace);703 defer dg.gpa.free(fqn);
704
705 const llvm_type = try dg.llvmType(decl.ty);
706 const llvm_addrspace = dg.llvmAddressSpace(decl.@"addrspace");
707 const llvm_global = dg.object.llvm_module.addGlobalInAddressSpace(llvm_type, fqn, llvm_addrspace);
708 gop.value_ptr.* = llvm_global;
709 return llvm_global;
693 }710 }
694711
695 fn llvmAddressSpace(self: DeclGen, address_space: std.builtin.AddressSpace) c_uint {712 fn llvmAddressSpace(self: DeclGen, address_space: std.builtin.AddressSpace) c_uint {
...@@ -708,87 +725,87 @@ pub const DeclGen = struct {...@@ -708,87 +725,87 @@ pub const DeclGen = struct {
708 };725 };
709 }726 }
710727
711 fn llvmType(self: *DeclGen, t: Type) error{ OutOfMemory, CodegenFail }!*const llvm.Type {728 fn llvmType(dg: *DeclGen, t: Type) Error!*const llvm.Type {
712 const gpa = self.gpa;729 const gpa = dg.gpa;
713 log.debug("llvmType for {}", .{t});730 log.debug("llvmType for {}", .{t});
714 switch (t.zigTypeTag()) {731 switch (t.zigTypeTag()) {
715 .Void, .NoReturn => return self.context.voidType(),732 .Void, .NoReturn => return dg.context.voidType(),
716 .Int => {733 .Int => {
717 const info = t.intInfo(self.module.getTarget());734 const info = t.intInfo(dg.module.getTarget());
718 return self.context.intType(info.bits);735 return dg.context.intType(info.bits);
719 },736 },
720 .Enum => {737 .Enum => {
721 var buffer: Type.Payload.Bits = undefined;738 var buffer: Type.Payload.Bits = undefined;
722 const int_ty = t.intTagType(&buffer);739 const int_ty = t.intTagType(&buffer);
723 const bit_count = int_ty.intInfo(self.module.getTarget()).bits;740 const bit_count = int_ty.intInfo(dg.module.getTarget()).bits;
724 return self.context.intType(bit_count);741 return dg.context.intType(bit_count);
725 },742 },
726 .Float => switch (t.floatBits(self.module.getTarget())) {743 .Float => switch (t.floatBits(dg.module.getTarget())) {
727 16 => return self.context.halfType(),744 16 => return dg.context.halfType(),
728 32 => return self.context.floatType(),745 32 => return dg.context.floatType(),
729 64 => return self.context.doubleType(),746 64 => return dg.context.doubleType(),
730 80 => return self.context.x86FP80Type(),747 80 => return dg.context.x86FP80Type(),
731 128 => return self.context.fp128Type(),748 128 => return dg.context.fp128Type(),
732 else => unreachable,749 else => unreachable,
733 },750 },
734 .Bool => return self.context.intType(1),751 .Bool => return dg.context.intType(1),
735 .Pointer => {752 .Pointer => {
736 if (t.isSlice()) {753 if (t.isSlice()) {
737 var buf: Type.SlicePtrFieldTypeBuffer = undefined;754 var buf: Type.SlicePtrFieldTypeBuffer = undefined;
738 const ptr_type = t.slicePtrFieldType(&buf);755 const ptr_type = t.slicePtrFieldType(&buf);
739756
740 const fields: [2]*const llvm.Type = .{757 const fields: [2]*const llvm.Type = .{
741 try self.llvmType(ptr_type),758 try dg.llvmType(ptr_type),
742 try self.llvmType(Type.initTag(.usize)),759 try dg.llvmType(Type.initTag(.usize)),
743 };760 };
744 return self.context.structType(&fields, fields.len, .False);761 return dg.context.structType(&fields, fields.len, .False);
745 } else {762 } else {
746 const elem_type = try self.llvmType(t.elemType());763 const elem_type = try dg.llvmType(t.elemType());
747 const llvm_addrspace = self.llvmAddressSpace(t.ptrAddressSpace());764 const llvm_addrspace = dg.llvmAddressSpace(t.ptrAddressSpace());
748 return elem_type.pointerType(llvm_addrspace);765 return elem_type.pointerType(llvm_addrspace);
749 }766 }
750 },767 },
751 .Array => {768 .Array => {
752 const elem_type = try self.llvmType(t.elemType());769 const elem_type = try dg.llvmType(t.elemType());
753 const total_len = t.arrayLen() + @boolToInt(t.sentinel() != null);770 const total_len = t.arrayLen() + @boolToInt(t.sentinel() != null);
754 return elem_type.arrayType(@intCast(c_uint, total_len));771 return elem_type.arrayType(@intCast(c_uint, total_len));
755 },772 },
756 .Optional => {773 .Optional => {
757 var buf: Type.Payload.ElemType = undefined;774 var buf: Type.Payload.ElemType = undefined;
758 const child_type = t.optionalChild(&buf);775 const child_type = t.optionalChild(&buf);
759 const payload_llvm_ty = try self.llvmType(child_type);776 const payload_llvm_ty = try dg.llvmType(child_type);
760777
761 if (t.isPtrLikeOptional()) {778 if (t.isPtrLikeOptional()) {
762 return payload_llvm_ty;779 return payload_llvm_ty;
763 }780 }
764781
765 const fields: [2]*const llvm.Type = .{782 const fields: [2]*const llvm.Type = .{
766 payload_llvm_ty, self.context.intType(1),783 payload_llvm_ty, dg.context.intType(1),
767 };784 };
768 return self.context.structType(&fields, fields.len, .False);785 return dg.context.structType(&fields, fields.len, .False);
769 },786 },
770 .ErrorUnion => {787 .ErrorUnion => {
771 const error_type = t.errorUnionSet();788 const error_type = t.errorUnionSet();
772 const payload_type = t.errorUnionPayload();789 const payload_type = t.errorUnionPayload();
773 const llvm_error_type = try self.llvmType(error_type);790 const llvm_error_type = try dg.llvmType(error_type);
774 if (!payload_type.hasCodeGenBits()) {791 if (!payload_type.hasCodeGenBits()) {
775 return llvm_error_type;792 return llvm_error_type;
776 }793 }
777 const llvm_payload_type = try self.llvmType(payload_type);794 const llvm_payload_type = try dg.llvmType(payload_type);
778795
779 const fields: [2]*const llvm.Type = .{ llvm_error_type, llvm_payload_type };796 const fields: [2]*const llvm.Type = .{ llvm_error_type, llvm_payload_type };
780 return self.context.structType(&fields, fields.len, .False);797 return dg.context.structType(&fields, fields.len, .False);
781 },798 },
782 .ErrorSet => {799 .ErrorSet => {
783 return self.context.intType(16);800 return dg.context.intType(16);
784 },801 },
785 .Struct => {802 .Struct => {
786 const gop = try self.object.type_map.getOrPut(gpa, t);803 const gop = try dg.object.type_map.getOrPut(gpa, t);
787 if (gop.found_existing) return gop.value_ptr.*;804 if (gop.found_existing) return gop.value_ptr.*;
788805
789 // The Type memory is ephemeral; since we want to store a longer-lived806 // The Type memory is ephemeral; since we want to store a longer-lived
790 // reference, we need to copy it here.807 // reference, we need to copy it here.
791 gop.key_ptr.* = try t.copy(&self.object.type_map_arena.allocator);808 gop.key_ptr.* = try t.copy(&dg.object.type_map_arena.allocator);
792809
793 const struct_obj = t.castTag(.@"struct").?.data;810 const struct_obj = t.castTag(.@"struct").?.data;
794 assert(struct_obj.haveFieldTypes());811 assert(struct_obj.haveFieldTypes());
...@@ -796,7 +813,7 @@ pub const DeclGen = struct {...@@ -796,7 +813,7 @@ pub const DeclGen = struct {
796 const name = try struct_obj.getFullyQualifiedName(gpa);813 const name = try struct_obj.getFullyQualifiedName(gpa);
797 defer gpa.free(name);814 defer gpa.free(name);
798815
799 const llvm_struct_ty = self.context.structCreateNamed(name);816 const llvm_struct_ty = dg.context.structCreateNamed(name);
800 gop.value_ptr.* = llvm_struct_ty; // must be done before any recursive calls817 gop.value_ptr.* = llvm_struct_ty; // must be done before any recursive calls
801818
802 var llvm_field_types: std.ArrayListUnmanaged(*const llvm.Type) = .{};819 var llvm_field_types: std.ArrayListUnmanaged(*const llvm.Type) = .{};
...@@ -805,7 +822,7 @@ pub const DeclGen = struct {...@@ -805,7 +822,7 @@ pub const DeclGen = struct {
805822
806 for (struct_obj.fields.values()) |field| {823 for (struct_obj.fields.values()) |field| {
807 if (!field.ty.hasCodeGenBits()) continue;824 if (!field.ty.hasCodeGenBits()) continue;
808 llvm_field_types.appendAssumeCapacity(try self.llvmType(field.ty));825 llvm_field_types.appendAssumeCapacity(try dg.llvmType(field.ty));
809 }826 }
810827
811 llvm_struct_ty.structSetBody(828 llvm_struct_ty.structSetBody(
...@@ -821,42 +838,56 @@ pub const DeclGen = struct {...@@ -821,42 +838,56 @@ pub const DeclGen = struct {
821 assert(union_obj.haveFieldTypes());838 assert(union_obj.haveFieldTypes());
822839
823 const enum_tag_ty = union_obj.tag_ty;840 const enum_tag_ty = union_obj.tag_ty;
824 const enum_tag_llvm_ty = try self.llvmType(enum_tag_ty);841 const enum_tag_llvm_ty = try dg.llvmType(enum_tag_ty);
825842
826 if (union_obj.onlyTagHasCodegenBits()) {843 if (union_obj.onlyTagHasCodegenBits()) {
827 return enum_tag_llvm_ty;844 return enum_tag_llvm_ty;
828 }845 }
829846
830 const target = self.module.getTarget();847 const target = dg.module.getTarget();
831 const most_aligned_field_index = union_obj.mostAlignedField(target);848 const most_aligned_field_index = union_obj.mostAlignedField(target);
832 const most_aligned_field = union_obj.fields.values()[most_aligned_field_index];849 const most_aligned_field = union_obj.fields.values()[most_aligned_field_index];
833 // TODO handle when the most aligned field is different than the850 // TODO handle when the most aligned field is different than the
834 // biggest sized field.851 // biggest sized field.
835852
836 const llvm_fields = [_]*const llvm.Type{853 const llvm_fields = [_]*const llvm.Type{
837 try self.llvmType(most_aligned_field.ty),854 try dg.llvmType(most_aligned_field.ty),
838 enum_tag_llvm_ty,855 enum_tag_llvm_ty,
839 };856 };
840 return self.context.structType(&llvm_fields, llvm_fields.len, .False);857 return dg.context.structType(&llvm_fields, llvm_fields.len, .False);
841 },858 },
842 .Fn => {859 .Fn => {
843 const ret_ty = try self.llvmType(t.fnReturnType());860 const fn_info = t.fnInfo();
844 const params_len = t.fnParamLen();861 const target = dg.module.getTarget();
845 const llvm_params = try gpa.alloc(*const llvm.Type, params_len);862 const sret = firstParamSRet(fn_info, target);
846 defer gpa.free(llvm_params);863 const return_type = fn_info.return_type;
847 for (llvm_params) |*llvm_param, i| {864 const raw_llvm_ret_ty = try dg.llvmType(return_type);
848 llvm_param.* = try self.llvmType(t.fnParamType(i));865 const llvm_ret_ty = if (!return_type.hasCodeGenBits() or sret)
866 dg.context.voidType()
867 else
868 raw_llvm_ret_ty;
869
870 var llvm_params = std.ArrayList(*const llvm.Type).init(dg.gpa);
871 defer llvm_params.deinit();
872
873 if (sret) {
874 try llvm_params.append(raw_llvm_ret_ty.pointerType(0));
875 }
876
877 for (fn_info.param_types) |param_ty| {
878 if (!param_ty.hasCodeGenBits()) continue;
879
880 const raw_llvm_ty = try dg.llvmType(param_ty);
881 const actual_llvm_ty = if (!isByRef(param_ty)) raw_llvm_ty else raw_llvm_ty.pointerType(0);
882 try llvm_params.append(actual_llvm_ty);
849 }883 }
850 const is_var_args = t.fnIsVarArgs();884
851 const llvm_fn_ty = llvm.functionType(885 return llvm.functionType(
852 ret_ty,886 llvm_ret_ty,
853 llvm_params.ptr,887 llvm_params.items.ptr,
854 @intCast(c_uint, llvm_params.len),888 @intCast(c_uint, llvm_params.items.len),
855 llvm.Bool.fromBool(is_var_args),889 llvm.Bool.fromBool(fn_info.is_var_args),
856 );890 );
857 // TODO make .Fn not both a pointer type and a prototype
858 const llvm_addrspace = self.llvmAddressSpace(.generic);
859 return llvm_fn_ty.pointerType(llvm_addrspace);
860 },891 },
861 .ComptimeInt => unreachable,892 .ComptimeInt => unreachable,
862 .ComptimeFloat => unreachable,893 .ComptimeFloat => unreachable,
...@@ -871,11 +902,11 @@ pub const DeclGen = struct {...@@ -871,11 +902,11 @@ pub const DeclGen = struct {
871 .Frame,902 .Frame,
872 .AnyFrame,903 .AnyFrame,
873 .Vector,904 .Vector,
874 => return self.todo("implement llvmType for type '{}'", .{t}),905 => return dg.todo("implement llvmType for type '{}'", .{t}),
875 }906 }
876 }907 }
877908
878 fn genTypedValue(self: *DeclGen, tv: TypedValue) error{ OutOfMemory, CodegenFail }!*const llvm.Value {909 fn genTypedValue(self: *DeclGen, tv: TypedValue) Error!*const llvm.Value {
879 if (tv.val.isUndef()) {910 if (tv.val.isUndef()) {
880 const llvm_type = try self.llvmType(tv.ty);911 const llvm_type = try self.llvmType(tv.ty);
881 return llvm_type.getUndef();912 return llvm_type.getUndef();
...@@ -961,9 +992,12 @@ pub const DeclGen = struct {...@@ -961,9 +992,12 @@ pub const DeclGen = struct {
961 } else {992 } else {
962 const decl = tv.val.castTag(.decl_ref).?.data;993 const decl = tv.val.castTag(.decl_ref).?.data;
963 decl.alive = true;994 decl.alive = true;
964 const val = try self.resolveGlobalDecl(decl);
965 const llvm_type = try self.llvmType(tv.ty);995 const llvm_type = try self.llvmType(tv.ty);
966 return val.constBitCast(llvm_type);996 const llvm_val = if (decl.ty.zigTypeTag() == .Fn)
997 try self.resolveLlvmFunction(decl)
998 else
999 try self.resolveGlobalDecl(decl);
1000 return llvm_val.constBitCast(llvm_type);
967 }1001 }
968 },1002 },
969 .variable => {1003 .variable => {
...@@ -1047,17 +1081,23 @@ pub const DeclGen = struct {...@@ -1047,17 +1081,23 @@ pub const DeclGen = struct {
1047 return self.todo("handle more array values", .{});1081 return self.todo("handle more array values", .{});
1048 },1082 },
1049 .Optional => {1083 .Optional => {
1084 var buf: Type.Payload.ElemType = undefined;
1085 const payload_ty = tv.ty.optionalChild(&buf);
1086
1050 if (tv.ty.isPtrLikeOptional()) {1087 if (tv.ty.isPtrLikeOptional()) {
1051 return self.todo("implement const of optional pointer", .{});1088 if (tv.val.castTag(.opt_payload)) |payload| {
1089 return self.genTypedValue(.{ .ty = payload_ty, .val = payload.data });
1090 } else {
1091 const llvm_ty = try self.llvmType(tv.ty);
1092 return llvm_ty.constNull();
1093 }
1052 }1094 }
1053 var buf: Type.Payload.ElemType = undefined;
1054 const payload_type = tv.ty.optionalChild(&buf);
1055 const is_pl = !tv.val.isNull();1095 const is_pl = !tv.val.isNull();
1056 const llvm_i1 = self.context.intType(1);1096 const llvm_i1 = self.context.intType(1);
10571097
1058 const fields: [2]*const llvm.Value = .{1098 const fields: [2]*const llvm.Value = .{
1059 try self.genTypedValue(.{1099 try self.genTypedValue(.{
1060 .ty = payload_type,1100 .ty = payload_ty,
1061 .val = if (tv.val.castTag(.opt_payload)) |pl| pl.data else Value.initTag(.undef),1101 .val = if (tv.val.castTag(.opt_payload)) |pl| pl.data else Value.initTag(.undef),
1062 }),1102 }),
1063 if (is_pl) llvm_i1.constAllOnes() else llvm_i1.constNull(),1103 if (is_pl) llvm_i1.constAllOnes() else llvm_i1.constNull(),
...@@ -1068,7 +1108,6 @@ pub const DeclGen = struct {...@@ -1068,7 +1108,6 @@ pub const DeclGen = struct {
1068 const fn_decl = switch (tv.val.tag()) {1108 const fn_decl = switch (tv.val.tag()) {
1069 .extern_fn => tv.val.castTag(.extern_fn).?.data,1109 .extern_fn => tv.val.castTag(.extern_fn).?.data,
1070 .function => tv.val.castTag(.function).?.data.owner_decl,1110 .function => tv.val.castTag(.function).?.data.owner_decl,
1071 .decl_ref => tv.val.castTag(.decl_ref).?.data,
1072 else => unreachable,1111 else => unreachable,
1073 };1112 };
1074 fn_decl.alive = true;1113 fn_decl.alive = true;
...@@ -1153,10 +1192,14 @@ pub const DeclGen = struct {...@@ -1153,10 +1192,14 @@ pub const DeclGen = struct {
1153 }1192 }
1154 }1193 }
11551194
1156 fn addAttr(dg: *DeclGen, val: *const llvm.Value, index: llvm.AttributeIndex, name: []const u8) void {1195 fn addAttr(dg: DeclGen, val: *const llvm.Value, index: llvm.AttributeIndex, name: []const u8) void {
1157 return dg.addAttrInt(val, index, name, 0);1196 return dg.addAttrInt(val, index, name, 0);
1158 }1197 }
11591198
1199 fn addArgAttr(dg: DeclGen, fn_val: *const llvm.Value, param_index: u32, attr_name: []const u8) void {
1200 return dg.addAttr(fn_val, param_index + 1, attr_name);
1201 }
1202
1160 fn removeAttr(val: *const llvm.Value, index: llvm.AttributeIndex, name: []const u8) void {1203 fn removeAttr(val: *const llvm.Value, index: llvm.AttributeIndex, name: []const u8) void {
1161 const kind_id = llvm.getEnumAttributeKindForName(name.ptr, name.len);1204 const kind_id = llvm.getEnumAttributeKindForName(name.ptr, name.len);
1162 assert(kind_id != 0);1205 assert(kind_id != 0);
...@@ -1164,7 +1207,7 @@ pub const DeclGen = struct {...@@ -1164,7 +1207,7 @@ pub const DeclGen = struct {
1164 }1207 }
11651208
1166 fn addAttrInt(1209 fn addAttrInt(
1167 dg: *DeclGen,1210 dg: DeclGen,
1168 val: *const llvm.Value,1211 val: *const llvm.Value,
1169 index: llvm.AttributeIndex,1212 index: llvm.AttributeIndex,
1170 name: []const u8,1213 name: []const u8,
...@@ -1176,7 +1219,7 @@ pub const DeclGen = struct {...@@ -1176,7 +1219,7 @@ pub const DeclGen = struct {
1176 val.addAttributeAtIndex(index, llvm_attr);1219 val.addAttributeAtIndex(index, llvm_attr);
1177 }1220 }
11781221
1179 fn addFnAttr(dg: *DeclGen, val: *const llvm.Value, name: []const u8) void {1222 fn addFnAttr(dg: DeclGen, val: *const llvm.Value, name: []const u8) void {
1180 dg.addAttr(val, std.math.maxInt(llvm.AttributeIndex), name);1223 dg.addAttr(val, std.math.maxInt(llvm.AttributeIndex), name);
1181 }1224 }
11821225
...@@ -1184,7 +1227,7 @@ pub const DeclGen = struct {...@@ -1184,7 +1227,7 @@ pub const DeclGen = struct {
1184 removeAttr(fn_val, std.math.maxInt(llvm.AttributeIndex), name);1227 removeAttr(fn_val, std.math.maxInt(llvm.AttributeIndex), name);
1185 }1228 }
11861229
1187 fn addFnAttrInt(dg: *DeclGen, fn_val: *const llvm.Value, name: []const u8, int: u64) void {1230 fn addFnAttrInt(dg: DeclGen, fn_val: *const llvm.Value, name: []const u8, int: u64) void {
1188 return dg.addAttrInt(fn_val, std.math.maxInt(llvm.AttributeIndex), name, int);1231 return dg.addAttrInt(fn_val, std.math.maxInt(llvm.AttributeIndex), name, int);
1189 }1232 }
11901233
...@@ -1227,8 +1270,12 @@ pub const FuncGen = struct {...@@ -1227,8 +1270,12 @@ pub const FuncGen = struct {
1227 /// in other instructions. This table is cleared before every function is generated.1270 /// in other instructions. This table is cleared before every function is generated.
1228 func_inst_table: std.AutoHashMapUnmanaged(Air.Inst.Index, *const llvm.Value),1271 func_inst_table: std.AutoHashMapUnmanaged(Air.Inst.Index, *const llvm.Value),
12291272
1273 /// If the return type isByRef, this is the result pointer. Otherwise null.
1274 ret_ptr: ?*const llvm.Value,
1230 /// These fields are used to refer to the LLVM value of the function parameters1275 /// These fields are used to refer to the LLVM value of the function parameters
1231 /// in an Arg instruction.1276 /// in an Arg instruction.
1277 /// This list may be shorter than the list according to the zig type system;
1278 /// it omits 0-bit types.
1232 args: []*const llvm.Value,1279 args: []*const llvm.Value,
1233 arg_index: usize,1280 arg_index: usize,
12341281
...@@ -1258,7 +1305,7 @@ pub const FuncGen = struct {...@@ -1258,7 +1305,7 @@ pub const FuncGen = struct {
1258 self.blocks.deinit(self.gpa);1305 self.blocks.deinit(self.gpa);
1259 }1306 }
12601307
1261 fn todo(self: *FuncGen, comptime format: []const u8, args: anytype) error{ OutOfMemory, CodegenFail } {1308 fn todo(self: *FuncGen, comptime format: []const u8, args: anytype) Error {
1262 @setCold(true);1309 @setCold(true);
1263 return self.dg.todo(format, args);1310 return self.dg.todo(format, args);
1264 }1311 }
...@@ -1269,13 +1316,25 @@ pub const FuncGen = struct {...@@ -1269,13 +1316,25 @@ pub const FuncGen = struct {
12691316
1270 fn resolveInst(self: *FuncGen, inst: Air.Inst.Ref) !*const llvm.Value {1317 fn resolveInst(self: *FuncGen, inst: Air.Inst.Ref) !*const llvm.Value {
1271 if (self.air.value(inst)) |val| {1318 if (self.air.value(inst)) |val| {
1272 return self.dg.genTypedValue(.{ .ty = self.air.typeOf(inst), .val = val });1319 const ty = self.air.typeOf(inst);
1320 const llvm_val = try self.dg.genTypedValue(.{ .ty = ty, .val = val });
1321 if (!isByRef(ty)) return llvm_val;
1322
1323 // We have an LLVM value but we need to create a global constant and
1324 // set the value as its initializer, and then return a pointer to the global.
1325 const target = self.dg.module.getTarget();
1326 const global = self.dg.object.llvm_module.addGlobal(llvm_val.typeOf(), "");
1327 global.setInitializer(llvm_val);
1328 global.setLinkage(.Private);
1329 global.setGlobalConstant(.True);
1330 global.setAlignment(ty.abiAlignment(target));
1331 return global;
1273 }1332 }
1274 const inst_index = Air.refToIndex(inst).?;1333 const inst_index = Air.refToIndex(inst).?;
1275 return self.func_inst_table.get(inst_index).?;1334 return self.func_inst_table.get(inst_index).?;
1276 }1335 }
12771336
1278 fn genBody(self: *FuncGen, body: []const Air.Inst.Index) error{ OutOfMemory, CodegenFail }!void {1337 fn genBody(self: *FuncGen, body: []const Air.Inst.Index) Error!void {
1279 const air_tags = self.air.instructions.items(.tag);1338 const air_tags = self.air.instructions.items(.tag);
1280 for (body) |inst| {1339 for (body) |inst| {
1281 const opt_value: ?*const llvm.Value = switch (air_tags[inst]) {1340 const opt_value: ?*const llvm.Value = switch (air_tags[inst]) {
...@@ -1320,6 +1379,7 @@ pub const FuncGen = struct {...@@ -1320,6 +1379,7 @@ pub const FuncGen = struct {
1320 .is_err_ptr => try self.airIsErr(inst, .NE, true),1379 .is_err_ptr => try self.airIsErr(inst, .NE, true),
13211380
1322 .alloc => try self.airAlloc(inst),1381 .alloc => try self.airAlloc(inst),
1382 .ret_ptr => try self.airRetPtr(inst),
1323 .arg => try self.airArg(inst),1383 .arg => try self.airArg(inst),
1324 .bitcast => try self.airBitCast(inst),1384 .bitcast => try self.airBitCast(inst),
1325 .bool_to_int => try self.airBoolToInt(inst),1385 .bool_to_int => try self.airBoolToInt(inst),
...@@ -1338,6 +1398,7 @@ pub const FuncGen = struct {...@@ -1338,6 +1398,7 @@ pub const FuncGen = struct {
1338 .loop => try self.airLoop(inst),1398 .loop => try self.airLoop(inst),
1339 .not => try self.airNot(inst),1399 .not => try self.airNot(inst),
1340 .ret => try self.airRet(inst),1400 .ret => try self.airRet(inst),
1401 .ret_load => try self.airRetLoad(inst),
1341 .store => try self.airStore(inst),1402 .store => try self.airStore(inst),
1342 .assembly => try self.airAssembly(inst),1403 .assembly => try self.airAssembly(inst),
1343 .slice_ptr => try self.airSliceField(inst, 0),1404 .slice_ptr => try self.airSliceField(inst, 0),
...@@ -1370,6 +1431,7 @@ pub const FuncGen = struct {...@@ -1370,6 +1431,7 @@ pub const FuncGen = struct {
1370 .struct_field_ptr_index_2 => try self.airStructFieldPtrIndex(inst, 2),1431 .struct_field_ptr_index_2 => try self.airStructFieldPtrIndex(inst, 2),
1371 .struct_field_ptr_index_3 => try self.airStructFieldPtrIndex(inst, 3),1432 .struct_field_ptr_index_3 => try self.airStructFieldPtrIndex(inst, 3),
13721433
1434 .array_elem_val => try self.airArrayElemVal(inst),
1373 .slice_elem_val => try self.airSliceElemVal(inst),1435 .slice_elem_val => try self.airSliceElemVal(inst),
1374 .ptr_slice_elem_val => try self.airPtrSliceElemVal(inst),1436 .ptr_slice_elem_val => try self.airPtrSliceElemVal(inst),
1375 .ptr_elem_val => try self.airPtrElemVal(inst),1437 .ptr_elem_val => try self.airPtrElemVal(inst),
...@@ -1405,40 +1467,73 @@ pub const FuncGen = struct {...@@ -1405,40 +1467,73 @@ pub const FuncGen = struct {
1405 const pl_op = self.air.instructions.items(.data)[inst].pl_op;1467 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
1406 const extra = self.air.extraData(Air.Call, pl_op.payload);1468 const extra = self.air.extraData(Air.Call, pl_op.payload);
1407 const args = @bitCast([]const Air.Inst.Ref, self.air.extra[extra.end..][0..extra.data.args_len]);1469 const args = @bitCast([]const Air.Inst.Ref, self.air.extra[extra.end..][0..extra.data.args_len]);
1408 const zig_fn_type = self.air.typeOf(pl_op.operand);1470 const callee_ty = self.air.typeOf(pl_op.operand);
1409 const return_type = zig_fn_type.fnReturnType();1471 const zig_fn_ty = switch (callee_ty.zigTypeTag()) {
1472 .Fn => callee_ty,
1473 .Pointer => callee_ty.childType(),
1474 else => unreachable,
1475 };
1476 const fn_info = zig_fn_ty.fnInfo();
1477 const return_type = fn_info.return_type;
1478 const llvm_ret_ty = try self.dg.llvmType(return_type);
1410 const llvm_fn = try self.resolveInst(pl_op.operand);1479 const llvm_fn = try self.resolveInst(pl_op.operand);
1411 const target = self.dg.module.getTarget();1480 const target = self.dg.module.getTarget();
1481 const sret = firstParamSRet(fn_info, target);
14121482
1413 const llvm_param_vals = try self.gpa.alloc(*const llvm.Value, args.len);1483 var llvm_args = std.ArrayList(*const llvm.Value).init(self.gpa);
1414 defer self.gpa.free(llvm_param_vals);1484 defer llvm_args.deinit();
1485
1486 const ret_ptr = if (!sret) null else blk: {
1487 const ret_ptr = self.buildAlloca(llvm_ret_ty);
1488 ret_ptr.setAlignment(return_type.abiAlignment(target));
1489 try llvm_args.append(ret_ptr);
1490 break :blk ret_ptr;
1491 };
14151492
1416 for (args) |arg, i| {1493 for (args) |arg, i| {
1417 llvm_param_vals[i] = try self.resolveInst(arg);1494 const param_ty = fn_info.param_types[i];
1495 if (!param_ty.hasCodeGenBits()) continue;
1496
1497 try llvm_args.append(try self.resolveInst(arg));
1418 }1498 }
14191499
1420 const call = self.builder.buildCall(1500 const call = self.builder.buildCall(
1421 llvm_fn,1501 llvm_fn,
1422 llvm_param_vals.ptr,1502 llvm_args.items.ptr,
1423 @intCast(c_uint, args.len),1503 @intCast(c_uint, llvm_args.items.len),
1424 toLlvmCallConv(zig_fn_type.fnCallingConvention(), target),1504 toLlvmCallConv(zig_fn_ty.fnCallingConvention(), target),
1425 .Auto,1505 .Auto,
1426 "",1506 "",
1427 );1507 );
14281508
1429 if (return_type.isNoReturn()) {1509 if (return_type.isNoReturn()) {
1430 _ = self.builder.buildUnreachable();1510 _ = self.builder.buildUnreachable();
1511 return null;
1512 } else if (self.liveness.isUnused(inst) or !return_type.hasCodeGenBits()) {
1513 return null;
1514 } else if (sret) {
1515 call.setCallSret(llvm_ret_ty);
1516 return ret_ptr;
1517 } else {
1518 return call;
1431 }1519 }
1432
1433 // No need to store the LLVM value if the return type is void or noreturn
1434 if (!return_type.hasCodeGenBits()) return null;
1435
1436 return call;
1437 }1520 }
14381521
1439 fn airRet(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {1522 fn airRet(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
1440 const un_op = self.air.instructions.items(.data)[inst].un_op;1523 const un_op = self.air.instructions.items(.data)[inst].un_op;
1441 if (!self.air.typeOf(un_op).hasCodeGenBits()) {1524 const ret_ty = self.air.typeOf(un_op);
1525 if (self.ret_ptr) |ret_ptr| {
1526 const operand = try self.resolveInst(un_op);
1527 var ptr_ty_payload: Type.Payload.ElemType = .{
1528 .base = .{ .tag = .single_mut_pointer },
1529 .data = ret_ty,
1530 };
1531 const ptr_ty = Type.initPayload(&ptr_ty_payload.base);
1532 self.store(ret_ptr, ptr_ty, operand, .NotAtomic);
1533 _ = self.builder.buildRetVoid();
1534 return null;
1535 }
1536 if (!ret_ty.hasCodeGenBits()) {
1442 _ = self.builder.buildRetVoid();1537 _ = self.builder.buildRetVoid();
1443 return null;1538 return null;
1444 }1539 }
...@@ -1447,6 +1542,20 @@ pub const FuncGen = struct {...@@ -1447,6 +1542,20 @@ pub const FuncGen = struct {
1447 return null;1542 return null;
1448 }1543 }
14491544
1545 fn airRetLoad(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
1546 const un_op = self.air.instructions.items(.data)[inst].un_op;
1547 const ptr_ty = self.air.typeOf(un_op);
1548 const ret_ty = ptr_ty.childType();
1549 if (!ret_ty.hasCodeGenBits() or isByRef(ret_ty)) {
1550 _ = self.builder.buildRetVoid();
1551 return null;
1552 }
1553 const ptr = try self.resolveInst(un_op);
1554 const loaded = self.builder.buildLoad(ptr, "");
1555 _ = self.builder.buildRet(loaded);
1556 return null;
1557 }
1558
1450 fn airCmp(self: *FuncGen, inst: Air.Inst.Index, op: math.CompareOperator) !?*const llvm.Value {1559 fn airCmp(self: *FuncGen, inst: Air.Inst.Index, op: math.CompareOperator) !?*const llvm.Value {
1451 if (self.liveness.isUnused(inst))1560 if (self.liveness.isUnused(inst))
1452 return null;1561 return null;
...@@ -1491,19 +1600,18 @@ pub const FuncGen = struct {...@@ -1491,19 +1600,18 @@ pub const FuncGen = struct {
1491 const body = self.air.extra[extra.end..][0..extra.data.body_len];1600 const body = self.air.extra[extra.end..][0..extra.data.body_len];
1492 const parent_bb = self.context.createBasicBlock("Block");1601 const parent_bb = self.context.createBasicBlock("Block");
14931602
1494 // 5 breaks to a block seems like a reasonable default.1603 var break_bbs: BreakBasicBlocks = .{};
1495 var break_bbs = try BreakBasicBlocks.initCapacity(self.gpa, 5);1604 defer break_bbs.deinit(self.gpa);
1496 var break_vals = try BreakValues.initCapacity(self.gpa, 5);1605
1606 var break_vals: BreakValues = .{};
1607 defer break_vals.deinit(self.gpa);
1608
1497 try self.blocks.putNoClobber(self.gpa, inst, .{1609 try self.blocks.putNoClobber(self.gpa, inst, .{
1498 .parent_bb = parent_bb,1610 .parent_bb = parent_bb,
1499 .break_bbs = &break_bbs,1611 .break_bbs = &break_bbs,
1500 .break_vals = &break_vals,1612 .break_vals = &break_vals,
1501 });1613 });
1502 defer {1614 defer assert(self.blocks.remove(inst));
1503 assert(self.blocks.remove(inst));
1504 break_bbs.deinit(self.gpa);
1505 break_vals.deinit(self.gpa);
1506 }
15071615
1508 try self.genBody(body);1616 try self.genBody(body);
15091617
...@@ -1514,7 +1622,18 @@ pub const FuncGen = struct {...@@ -1514,7 +1622,18 @@ pub const FuncGen = struct {
1514 const inst_ty = self.air.typeOfIndex(inst);1622 const inst_ty = self.air.typeOfIndex(inst);
1515 if (!inst_ty.hasCodeGenBits()) return null;1623 if (!inst_ty.hasCodeGenBits()) return null;
15161624
1517 const phi_node = self.builder.buildPhi(try self.dg.llvmType(inst_ty), "");1625 const raw_llvm_ty = try self.dg.llvmType(inst_ty);
1626
1627 // If the zig tag type is a function, this represents an actual function body; not
1628 // a pointer to it. LLVM IR allows the call instruction to use function bodies instead
1629 // of function pointers, however the phi makes it a runtime value and therefore
1630 // the LLVM type has to be wrapped in a pointer.
1631 const llvm_ty = if (inst_ty.zigTypeTag() == .Fn)
1632 raw_llvm_ty.pointerType(0)
1633 else
1634 raw_llvm_ty;
1635
1636 const phi_node = self.builder.buildPhi(llvm_ty, "");
1518 phi_node.addIncoming(1637 phi_node.addIncoming(
1519 break_vals.items.ptr,1638 break_vals.items.ptr,
1520 break_bbs.items.ptr,1639 break_bbs.items.ptr,
...@@ -1657,25 +1776,23 @@ pub const FuncGen = struct {...@@ -1657,25 +1776,23 @@ pub const FuncGen = struct {
1657 }1776 }
16581777
1659 fn airSliceElemVal(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {1778 fn airSliceElemVal(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
1660 const is_volatile = false; // TODO
1661 if (!is_volatile and self.liveness.isUnused(inst))
1662 return null;
1663
1664 const bin_op = self.air.instructions.items(.data)[inst].bin_op;1779 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1665 const lhs = try self.resolveInst(bin_op.lhs);1780 const slice_ty = self.air.typeOf(bin_op.lhs);
1666 const rhs = try self.resolveInst(bin_op.rhs);1781 if (!slice_ty.isVolatilePtr() and self.liveness.isUnused(inst)) return null;
1667 const base_ptr = self.builder.buildExtractValue(lhs, 0, "");1782
1668 const indices: [1]*const llvm.Value = .{rhs};1783 const slice = try self.resolveInst(bin_op.lhs);
1784 const index = try self.resolveInst(bin_op.rhs);
1785 const base_ptr = self.builder.buildExtractValue(slice, 0, "");
1786 const indices: [1]*const llvm.Value = .{index};
1669 const ptr = self.builder.buildInBoundsGEP(base_ptr, &indices, indices.len, "");1787 const ptr = self.builder.buildInBoundsGEP(base_ptr, &indices, indices.len, "");
1670 return self.builder.buildLoad(ptr, "");1788 return self.load(ptr, slice_ty);
1671 }1789 }
16721790
1673 fn airPtrSliceElemVal(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {1791 fn airPtrSliceElemVal(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
1674 const is_volatile = false; // TODO
1675 if (!is_volatile and self.liveness.isUnused(inst))
1676 return null;
1677
1678 const bin_op = self.air.instructions.items(.data)[inst].bin_op;1792 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1793 const slice_ty = self.air.typeOf(bin_op.lhs).childType();
1794 if (!slice_ty.isVolatilePtr() and self.liveness.isUnused(inst)) return null;
1795
1679 const lhs = try self.resolveInst(bin_op.lhs);1796 const lhs = try self.resolveInst(bin_op.lhs);
1680 const rhs = try self.resolveInst(bin_op.rhs);1797 const rhs = try self.resolveInst(bin_op.rhs);
16811798
...@@ -1686,18 +1803,35 @@ pub const FuncGen = struct {...@@ -1686,18 +1803,35 @@ pub const FuncGen = struct {
16861803
1687 const indices: [1]*const llvm.Value = .{rhs};1804 const indices: [1]*const llvm.Value = .{rhs};
1688 const ptr = self.builder.buildInBoundsGEP(base_ptr, &indices, indices.len, "");1805 const ptr = self.builder.buildInBoundsGEP(base_ptr, &indices, indices.len, "");
1689 return self.builder.buildLoad(ptr, "");1806 return self.load(ptr, slice_ty);
1690 }1807 }
16911808
1692 fn airPtrElemVal(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {1809 fn airArrayElemVal(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
1693 const is_volatile = false; // TODO1810 if (self.liveness.isUnused(inst)) return null;
1694 if (!is_volatile and self.liveness.isUnused(inst))
1695 return null;
16961811
1697 const bin_op = self.air.instructions.items(.data)[inst].bin_op;1812 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1813 const array_ty = self.air.typeOf(bin_op.lhs);
1814 const array_llvm_val = try self.resolveInst(bin_op.lhs);
1815 const rhs = try self.resolveInst(bin_op.rhs);
1816 assert(isByRef(array_ty));
1817 const indices: [2]*const llvm.Value = .{ self.context.intType(32).constNull(), rhs };
1818 const elem_ptr = self.builder.buildInBoundsGEP(array_llvm_val, &indices, indices.len, "");
1819 const elem_ty = array_ty.childType();
1820 if (isByRef(elem_ty)) {
1821 return elem_ptr;
1822 } else {
1823 return self.builder.buildLoad(elem_ptr, "");
1824 }
1825 }
1826
1827 fn airPtrElemVal(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
1828 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1829 const ptr_ty = self.air.typeOf(bin_op.lhs);
1830 if (!ptr_ty.isVolatilePtr() and self.liveness.isUnused(inst)) return null;
1831
1698 const base_ptr = try self.resolveInst(bin_op.lhs);1832 const base_ptr = try self.resolveInst(bin_op.lhs);
1699 const rhs = try self.resolveInst(bin_op.rhs);1833 const rhs = try self.resolveInst(bin_op.rhs);
1700 const ptr = if (self.air.typeOf(bin_op.lhs).isSinglePointer()) ptr: {1834 const ptr = if (ptr_ty.isSinglePointer()) ptr: {
1701 // If this is a single-item pointer to an array, we need another index in the GEP.1835 // If this is a single-item pointer to an array, we need another index in the GEP.
1702 const indices: [2]*const llvm.Value = .{ self.context.intType(32).constNull(), rhs };1836 const indices: [2]*const llvm.Value = .{ self.context.intType(32).constNull(), rhs };
1703 break :ptr self.builder.buildInBoundsGEP(base_ptr, &indices, indices.len, "");1837 break :ptr self.builder.buildInBoundsGEP(base_ptr, &indices, indices.len, "");
...@@ -1705,7 +1839,7 @@ pub const FuncGen = struct {...@@ -1705,7 +1839,7 @@ pub const FuncGen = struct {
1705 const indices: [1]*const llvm.Value = .{rhs};1839 const indices: [1]*const llvm.Value = .{rhs};
1706 break :ptr self.builder.buildInBoundsGEP(base_ptr, &indices, indices.len, "");1840 break :ptr self.builder.buildInBoundsGEP(base_ptr, &indices, indices.len, "");
1707 };1841 };
1708 return self.builder.buildLoad(ptr, "");1842 return self.load(ptr, ptr_ty);
1709 }1843 }
17101844
1711 fn airPtrElemPtr(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {1845 fn airPtrElemPtr(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
...@@ -1727,17 +1861,16 @@ pub const FuncGen = struct {...@@ -1727,17 +1861,16 @@ pub const FuncGen = struct {
1727 }1861 }
17281862
1729 fn airPtrPtrElemVal(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {1863 fn airPtrPtrElemVal(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
1730 const is_volatile = false; // TODO
1731 if (!is_volatile and self.liveness.isUnused(inst))
1732 return null;
1733
1734 const bin_op = self.air.instructions.items(.data)[inst].bin_op;1864 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1865 const ptr_ty = self.air.typeOf(bin_op.lhs).childType();
1866 if (!ptr_ty.isVolatilePtr() and self.liveness.isUnused(inst)) return null;
1867
1735 const lhs = try self.resolveInst(bin_op.lhs);1868 const lhs = try self.resolveInst(bin_op.lhs);
1736 const rhs = try self.resolveInst(bin_op.rhs);1869 const rhs = try self.resolveInst(bin_op.rhs);
1737 const base_ptr = self.builder.buildLoad(lhs, "");1870 const base_ptr = self.builder.buildLoad(lhs, "");
1738 const indices: [1]*const llvm.Value = .{rhs};1871 const indices: [1]*const llvm.Value = .{rhs};
1739 const ptr = self.builder.buildInBoundsGEP(base_ptr, &indices, indices.len, "");1872 const ptr = self.builder.buildInBoundsGEP(base_ptr, &indices, indices.len, "");
1740 return self.builder.buildLoad(ptr, "");1873 return self.load(ptr, ptr_ty);
1741 }1874 }
17421875
1743 fn airStructFieldPtr(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {1876 fn airStructFieldPtr(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
...@@ -1770,9 +1903,19 @@ pub const FuncGen = struct {...@@ -1770,9 +1903,19 @@ pub const FuncGen = struct {
1770 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;1903 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
1771 const struct_field = self.air.extraData(Air.StructField, ty_pl.payload).data;1904 const struct_field = self.air.extraData(Air.StructField, ty_pl.payload).data;
1772 const struct_ty = self.air.typeOf(struct_field.struct_operand);1905 const struct_ty = self.air.typeOf(struct_field.struct_operand);
1773 const struct_byval = try self.resolveInst(struct_field.struct_operand);1906 const struct_llvm_val = try self.resolveInst(struct_field.struct_operand);
1774 const field_index = llvmFieldIndex(struct_ty, struct_field.field_index);1907 const field_index = llvmFieldIndex(struct_ty, struct_field.field_index);
1775 return self.builder.buildExtractValue(struct_byval, field_index, "");1908 if (isByRef(struct_ty)) {
1909 const field_ptr = self.builder.buildStructGEP(struct_llvm_val, field_index, "");
1910 const field_ty = struct_ty.structFieldType(struct_field.field_index);
1911 if (isByRef(field_ty)) {
1912 return field_ptr;
1913 } else {
1914 return self.builder.buildLoad(field_ptr, "");
1915 }
1916 } else {
1917 return self.builder.buildExtractValue(struct_llvm_val, field_index, "");
1918 }
1776 }1919 }
17771920
1778 fn airNot(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {1921 fn airNot(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
...@@ -2465,17 +2608,21 @@ pub const FuncGen = struct {...@@ -2465,17 +2608,21 @@ pub const FuncGen = struct {
2465 self.arg_index += 1;2608 self.arg_index += 1;
24662609
2467 const inst_ty = self.air.typeOfIndex(inst);2610 const inst_ty = self.air.typeOfIndex(inst);
2468 const ptr_val = self.buildAlloca(try self.dg.llvmType(inst_ty));2611 if (isByRef(inst_ty)) {
2469 _ = self.builder.buildStore(arg_val, ptr_val);2612 // TODO declare debug variable
2470 return self.builder.buildLoad(ptr_val, "");2613 return arg_val;
2614 } else {
2615 const ptr_val = self.buildAlloca(try self.dg.llvmType(inst_ty));
2616 _ = self.builder.buildStore(arg_val, ptr_val);
2617 // TODO declare debug variable
2618 return arg_val;
2619 }
2471 }2620 }
24722621
2473 fn airAlloc(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {2622 fn airAlloc(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
2474 if (self.liveness.isUnused(inst)) return null;2623 if (self.liveness.isUnused(inst)) return null;
2475 // buildAlloca expects the pointee type, not the pointer type, so assert that
2476 // a Payload.PointerSimple is passed to the alloc instruction.
2477 const ptr_ty = self.air.typeOfIndex(inst);2624 const ptr_ty = self.air.typeOfIndex(inst);
2478 const pointee_type = ptr_ty.elemType();2625 const pointee_type = ptr_ty.childType();
2479 if (!pointee_type.hasCodeGenBits()) return null;2626 if (!pointee_type.hasCodeGenBits()) return null;
2480 const pointee_llvm_ty = try self.dg.llvmType(pointee_type);2627 const pointee_llvm_ty = try self.dg.llvmType(pointee_type);
2481 const target = self.dg.module.getTarget();2628 const target = self.dg.module.getTarget();
...@@ -2484,6 +2631,19 @@ pub const FuncGen = struct {...@@ -2484,6 +2631,19 @@ pub const FuncGen = struct {
2484 return alloca_inst;2631 return alloca_inst;
2485 }2632 }
24862633
2634 fn airRetPtr(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
2635 if (self.liveness.isUnused(inst)) return null;
2636 const ptr_ty = self.air.typeOfIndex(inst);
2637 const ret_ty = ptr_ty.childType();
2638 if (!ret_ty.hasCodeGenBits()) return null;
2639 if (self.ret_ptr) |ret_ptr| return ret_ptr;
2640 const ret_llvm_ty = try self.dg.llvmType(ret_ty);
2641 const target = self.dg.module.getTarget();
2642 const alloca_inst = self.buildAlloca(ret_llvm_ty);
2643 alloca_inst.setAlignment(ptr_ty.ptrAlignment(target));
2644 return alloca_inst;
2645 }
2646
2487 /// Use this instead of builder.buildAlloca, because this function makes sure to2647 /// Use this instead of builder.buildAlloca, because this function makes sure to
2488 /// put the alloca instruction at the top of the function!2648 /// put the alloca instruction at the top of the function!
2489 fn buildAlloca(self: *FuncGen, t: *const llvm.Type) *const llvm.Value {2649 fn buildAlloca(self: *FuncGen, t: *const llvm.Type) *const llvm.Value {
...@@ -2513,7 +2673,7 @@ pub const FuncGen = struct {...@@ -2513,7 +2673,7 @@ pub const FuncGen = struct {
2513 const dest_ptr = try self.resolveInst(bin_op.lhs);2673 const dest_ptr = try self.resolveInst(bin_op.lhs);
2514 const ptr_ty = self.air.typeOf(bin_op.lhs);2674 const ptr_ty = self.air.typeOf(bin_op.lhs);
2515 const src_operand = try self.resolveInst(bin_op.rhs);2675 const src_operand = try self.resolveInst(bin_op.rhs);
2516 _ = self.store(dest_ptr, ptr_ty, src_operand);2676 self.store(dest_ptr, ptr_ty, src_operand, .NotAtomic);
2517 return null;2677 return null;
2518 }2678 }
25192679
...@@ -2658,11 +2818,11 @@ pub const FuncGen = struct {...@@ -2658,11 +2818,11 @@ pub const FuncGen = struct {
2658 if (opt_abi_ty) |abi_ty| {2818 if (opt_abi_ty) |abi_ty| {
2659 // operand needs widening and truncating2819 // operand needs widening and truncating
2660 const casted_ptr = self.builder.buildBitCast(ptr, abi_ty.pointerType(0), "");2820 const casted_ptr = self.builder.buildBitCast(ptr, abi_ty.pointerType(0), "");
2661 const load_inst = self.load(casted_ptr, ptr_ty);2821 const load_inst = self.load(casted_ptr, ptr_ty).?;
2662 load_inst.setOrdering(ordering);2822 load_inst.setOrdering(ordering);
2663 return self.builder.buildTrunc(load_inst, try self.dg.llvmType(operand_ty), "");2823 return self.builder.buildTrunc(load_inst, try self.dg.llvmType(operand_ty), "");
2664 }2824 }
2665 const load_inst = self.load(ptr, ptr_ty);2825 const load_inst = self.load(ptr, ptr_ty).?;
2666 load_inst.setOrdering(ordering);2826 load_inst.setOrdering(ordering);
2667 return load_inst;2827 return load_inst;
2668 }2828 }
...@@ -2673,10 +2833,11 @@ pub const FuncGen = struct {...@@ -2673,10 +2833,11 @@ pub const FuncGen = struct {
2673 ordering: llvm.AtomicOrdering,2833 ordering: llvm.AtomicOrdering,
2674 ) !?*const llvm.Value {2834 ) !?*const llvm.Value {
2675 const bin_op = self.air.instructions.items(.data)[inst].bin_op;2835 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
2676 var ptr = try self.resolveInst(bin_op.lhs);
2677 const ptr_ty = self.air.typeOf(bin_op.lhs);2836 const ptr_ty = self.air.typeOf(bin_op.lhs);
2837 const operand_ty = ptr_ty.childType();
2838 if (!operand_ty.hasCodeGenBits()) return null;
2839 var ptr = try self.resolveInst(bin_op.lhs);
2678 var element = try self.resolveInst(bin_op.rhs);2840 var element = try self.resolveInst(bin_op.rhs);
2679 const operand_ty = ptr_ty.elemType();
2680 const opt_abi_ty = self.dg.getAtomicAbiType(operand_ty, false);2841 const opt_abi_ty = self.dg.getAtomicAbiType(operand_ty, false);
26812842
2682 if (opt_abi_ty) |abi_ty| {2843 if (opt_abi_ty) |abi_ty| {
...@@ -2688,8 +2849,7 @@ pub const FuncGen = struct {...@@ -2688,8 +2849,7 @@ pub const FuncGen = struct {
2688 element = self.builder.buildZExt(element, abi_ty, "");2849 element = self.builder.buildZExt(element, abi_ty, "");
2689 }2850 }
2690 }2851 }
2691 const store_inst = self.store(ptr, ptr_ty, element);2852 self.store(ptr, ptr_ty, element, ordering);
2692 store_inst.setOrdering(ordering);
2693 return null;2853 return null;
2694 }2854 }
26952855
...@@ -2724,10 +2884,9 @@ pub const FuncGen = struct {...@@ -2724,10 +2884,9 @@ pub const FuncGen = struct {
2724 const src_ptr = try self.resolveInst(extra.lhs);2884 const src_ptr = try self.resolveInst(extra.lhs);
2725 const src_ptr_ty = self.air.typeOf(extra.lhs);2885 const src_ptr_ty = self.air.typeOf(extra.lhs);
2726 const len = try self.resolveInst(extra.rhs);2886 const len = try self.resolveInst(extra.rhs);
2727 const u8_llvm_ty = self.context.intType(8);2887 const llvm_ptr_u8 = self.context.intType(8).pointerType(0);
2728 const ptr_u8_llvm_ty = u8_llvm_ty.pointerType(0);2888 const dest_ptr_u8 = self.builder.buildBitCast(dest_ptr, llvm_ptr_u8, "");
2729 const dest_ptr_u8 = self.builder.buildBitCast(dest_ptr, ptr_u8_llvm_ty, "");2889 const src_ptr_u8 = self.builder.buildBitCast(src_ptr, llvm_ptr_u8, "");
2730 const src_ptr_u8 = self.builder.buildBitCast(src_ptr, ptr_u8_llvm_ty, "");
2731 const is_volatile = src_ptr_ty.isVolatilePtr() or dest_ptr_ty.isVolatilePtr();2890 const is_volatile = src_ptr_ty.isVolatilePtr() or dest_ptr_ty.isVolatilePtr();
2732 const target = self.dg.module.getTarget();2891 const target = self.dg.module.getTarget();
2733 _ = self.builder.buildMemCpy(2892 _ = self.builder.buildMemCpy(
...@@ -2843,7 +3002,10 @@ pub const FuncGen = struct {...@@ -2843,7 +3002,10 @@ pub const FuncGen = struct {
2843 return self.llvmModule().getIntrinsicDeclaration(id, null, 0);3002 return self.llvmModule().getIntrinsicDeclaration(id, null, 0);
2844 }3003 }
28453004
2846 fn load(self: *FuncGen, ptr: *const llvm.Value, ptr_ty: Type) *const llvm.Value {3005 fn load(self: *FuncGen, ptr: *const llvm.Value, ptr_ty: Type) ?*const llvm.Value {
3006 const pointee_ty = ptr_ty.childType();
3007 if (!pointee_ty.hasCodeGenBits()) return null;
3008 if (isByRef(pointee_ty)) return ptr;
2847 const llvm_inst = self.builder.buildLoad(ptr, "");3009 const llvm_inst = self.builder.buildLoad(ptr, "");
2848 const target = self.dg.module.getTarget();3010 const target = self.dg.module.getTarget();
2849 llvm_inst.setAlignment(ptr_ty.ptrAlignment(target));3011 llvm_inst.setAlignment(ptr_ty.ptrAlignment(target));
...@@ -2856,12 +3018,31 @@ pub const FuncGen = struct {...@@ -2856,12 +3018,31 @@ pub const FuncGen = struct {
2856 ptr: *const llvm.Value,3018 ptr: *const llvm.Value,
2857 ptr_ty: Type,3019 ptr_ty: Type,
2858 elem: *const llvm.Value,3020 elem: *const llvm.Value,
2859 ) *const llvm.Value {3021 ordering: llvm.AtomicOrdering,
2860 const llvm_inst = self.builder.buildStore(elem, ptr);3022 ) void {
3023 const elem_ty = ptr_ty.childType();
3024 if (!elem_ty.hasCodeGenBits()) {
3025 return;
3026 }
2861 const target = self.dg.module.getTarget();3027 const target = self.dg.module.getTarget();
2862 llvm_inst.setAlignment(ptr_ty.ptrAlignment(target));3028 if (!isByRef(elem_ty)) {
2863 llvm_inst.setVolatile(llvm.Bool.fromBool(ptr_ty.isVolatilePtr()));3029 const store_inst = self.builder.buildStore(elem, ptr);
2864 return llvm_inst;3030 store_inst.setOrdering(ordering);
3031 store_inst.setAlignment(ptr_ty.ptrAlignment(target));
3032 store_inst.setVolatile(llvm.Bool.fromBool(ptr_ty.isVolatilePtr()));
3033 return;
3034 }
3035 assert(ordering == .NotAtomic);
3036 const llvm_ptr_u8 = self.context.intType(8).pointerType(0);
3037 const size_bytes = elem_ty.abiSize(target);
3038 _ = self.builder.buildMemCpy(
3039 self.builder.buildBitCast(ptr, llvm_ptr_u8, ""),
3040 ptr_ty.ptrAlignment(target),
3041 self.builder.buildBitCast(elem, llvm_ptr_u8, ""),
3042 elem_ty.abiAlignment(target),
3043 self.context.intType(Type.usize.intInfo(target).bits).constInt(size_bytes, .False),
3044 ptr_ty.isVolatilePtr(),
3045 );
2865 }3046 }
2866};3047};
28673048
...@@ -3113,3 +3294,54 @@ fn llvmFieldIndex(ty: Type, index: u32) c_uint {...@@ -3113,3 +3294,54 @@ fn llvmFieldIndex(ty: Type, index: u32) c_uint {
3113 }3294 }
3114 return result;3295 return result;
3115}3296}
3297
3298fn firstParamSRet(fn_info: Type.Payload.Function.Data, target: std.Target) bool {
3299 switch (fn_info.cc) {
3300 .Unspecified, .Inline => return isByRef(fn_info.return_type),
3301 .C => {},
3302 else => return false,
3303 }
3304 switch (target.cpu.arch) {
3305 .mips, .mipsel => return false,
3306 .x86_64 => switch (target.os.tag) {
3307 .windows => return @import("../arch/x86_64/abi.zig").classifyWindows(fn_info.return_type, target) == .memory,
3308 else => return @import("../arch/x86_64/abi.zig").classifySystemV(fn_info.return_type, target)[0] == .memory,
3309 },
3310 else => return false, // TODO investigate C ABI for other architectures
3311 }
3312}
3313
3314fn isByRef(ty: Type) bool {
3315 switch (ty.zigTypeTag()) {
3316 .Type,
3317 .ComptimeInt,
3318 .ComptimeFloat,
3319 .EnumLiteral,
3320 .Undefined,
3321 .Null,
3322 .BoundFn,
3323 .Opaque,
3324 => unreachable,
3325
3326 .NoReturn,
3327 .Void,
3328 .Bool,
3329 .Int,
3330 .Float,
3331 .Pointer,
3332 .ErrorSet,
3333 .Fn,
3334 .Enum,
3335 .Vector,
3336 .AnyFrame,
3337 => return false,
3338
3339 .Array, .Struct, .Frame => return ty.hasCodeGenBits(),
3340 .Union => return ty.hasCodeGenBits(),
3341 .ErrorUnion => return isByRef(ty.errorUnionPayload()),
3342 .Optional => {
3343 var buf: Type.Payload.ElemType = undefined;
3344 return isByRef(ty.optionalChild(&buf));
3345 },
3346 }
3347}
src/codegen/llvm/bindings.zig+12-6
...@@ -163,6 +163,18 @@ pub const Value = opaque {...@@ -163,6 +163,18 @@ pub const Value = opaque {
163163
164 pub const deleteFunction = LLVMDeleteFunction;164 pub const deleteFunction = LLVMDeleteFunction;
165 extern fn LLVMDeleteFunction(Fn: *const Value) void;165 extern fn LLVMDeleteFunction(Fn: *const Value) void;
166
167 pub const addSretAttr = ZigLLVMAddSretAttr;
168 extern fn ZigLLVMAddSretAttr(fn_ref: *const Value, ArgNo: c_uint, type_val: *const Type) void;
169
170 pub const setCallSret = ZigLLVMSetCallSret;
171 extern fn ZigLLVMSetCallSret(Call: *const Value, return_type: *const Type) void;
172
173 pub const getParam = LLVMGetParam;
174 extern fn LLVMGetParam(Fn: *const Value, Index: c_uint) *const Value;
175
176 pub const setInitializer = LLVMSetInitializer;
177 extern fn LLVMSetInitializer(GlobalVar: *const Value, ConstantVal: *const Value) void;
166};178};
167179
168pub const Type = opaque {180pub const Type = opaque {
...@@ -292,12 +304,6 @@ pub const VerifierFailureAction = enum(c_int) {...@@ -292,12 +304,6 @@ pub const VerifierFailureAction = enum(c_int) {
292pub const constNeg = LLVMConstNeg;304pub const constNeg = LLVMConstNeg;
293extern fn LLVMConstNeg(ConstantVal: *const Value) *const Value;305extern fn LLVMConstNeg(ConstantVal: *const Value) *const Value;
294306
295pub const setInitializer = LLVMSetInitializer;
296extern fn LLVMSetInitializer(GlobalVar: *const Value, ConstantVal: *const Value) void;
297
298pub const getParam = LLVMGetParam;
299extern fn LLVMGetParam(Fn: *const Value, Index: c_uint) *const Value;
300
301pub const getEnumAttributeKindForName = LLVMGetEnumAttributeKindForName;307pub const getEnumAttributeKindForName = LLVMGetEnumAttributeKindForName;
302extern fn LLVMGetEnumAttributeKindForName(Name: [*]const u8, SLen: usize) c_uint;308extern fn LLVMGetEnumAttributeKindForName(Name: [*]const u8, SLen: usize) c_uint;
303309
src/print_air.zig+3
...@@ -128,6 +128,7 @@ const Writer = struct {...@@ -128,6 +128,7 @@ const Writer = struct {
128 .bool_and,128 .bool_and,
129 .bool_or,129 .bool_or,
130 .store,130 .store,
131 .array_elem_val,
131 .slice_elem_val,132 .slice_elem_val,
132 .ptr_slice_elem_val,133 .ptr_slice_elem_val,
133 .ptr_elem_val,134 .ptr_elem_val,
...@@ -150,6 +151,7 @@ const Writer = struct {...@@ -150,6 +151,7 @@ const Writer = struct {
150 .ptrtoint,151 .ptrtoint,
151 .bool_to_int,152 .bool_to_int,
152 .ret,153 .ret,
154 .ret_load,
153 => try w.writeUnOp(s, inst),155 => try w.writeUnOp(s, inst),
154156
155 .breakpoint,157 .breakpoint,
...@@ -158,6 +160,7 @@ const Writer = struct {...@@ -158,6 +160,7 @@ const Writer = struct {
158160
159 .const_ty,161 .const_ty,
160 .alloc,162 .alloc,
163 .ret_ptr,
161 => try w.writeTy(s, inst),164 => try w.writeTy(s, inst),
162165
163 .not,166 .not,
src/type.zig+39-37
...@@ -1707,32 +1707,10 @@ pub const Type = extern union {...@@ -1707,32 +1707,10 @@ pub const Type = extern union {
1707 const int_tag_ty = self.intTagType(&buffer);1707 const int_tag_ty = self.intTagType(&buffer);
1708 return int_tag_ty.abiAlignment(target);1708 return int_tag_ty.abiAlignment(target);
1709 },1709 },
1710 .union_tagged => {1710 // TODO pass `true` for have_tag when unions have a safety tag
1711 const union_obj = self.castTag(.union_tagged).?.data;1711 .@"union" => return self.castTag(.@"union").?.data.abiAlignment(target, false),
1712 var biggest: u32 = union_obj.tag_ty.abiAlignment(target);1712 .union_tagged => return self.castTag(.union_tagged).?.data.abiAlignment(target, true),
1713 for (union_obj.fields.values()) |field| {1713
1714 if (!field.ty.hasCodeGenBits()) continue;
1715 const field_align = field.ty.abiAlignment(target);
1716 if (field_align > biggest) {
1717 biggest = field_align;
1718 }
1719 }
1720 assert(biggest != 0);
1721 return biggest;
1722 },
1723 .@"union" => {
1724 const union_obj = self.castTag(.@"union").?.data;
1725 var biggest: u32 = 0;
1726 for (union_obj.fields.values()) |field| {
1727 if (!field.ty.hasCodeGenBits()) continue;
1728 const field_align = field.ty.abiAlignment(target);
1729 if (field_align > biggest) {
1730 biggest = field_align;
1731 }
1732 }
1733 assert(biggest != 0);
1734 return biggest;
1735 },
1736 .c_void,1714 .c_void,
1737 .void,1715 .void,
1738 .type,1716 .type,
...@@ -1790,6 +1768,7 @@ pub const Type = extern union {...@@ -1790,6 +1768,7 @@ pub const Type = extern union {
1790 const is_packed = s.layout == .Packed;1768 const is_packed = s.layout == .Packed;
1791 if (is_packed) @panic("TODO packed structs");1769 if (is_packed) @panic("TODO packed structs");
1792 var size: u64 = 0;1770 var size: u64 = 0;
1771 var big_align: u32 = 0;
1793 for (s.fields.values()) |field| {1772 for (s.fields.values()) |field| {
1794 if (!field.ty.hasCodeGenBits()) continue;1773 if (!field.ty.hasCodeGenBits()) continue;
17951774
...@@ -1797,12 +1776,14 @@ pub const Type = extern union {...@@ -1797,12 +1776,14 @@ pub const Type = extern union {
1797 if (field.abi_align.tag() == .abi_align_default) {1776 if (field.abi_align.tag() == .abi_align_default) {
1798 break :a field.ty.abiAlignment(target);1777 break :a field.ty.abiAlignment(target);
1799 } else {1778 } else {
1800 break :a field.abi_align.toUnsignedInt();1779 break :a @intCast(u32, field.abi_align.toUnsignedInt());
1801 }1780 }
1802 };1781 };
1782 big_align = @maximum(big_align, field_align);
1803 size = std.mem.alignForwardGeneric(u64, size, field_align);1783 size = std.mem.alignForwardGeneric(u64, size, field_align);
1804 size += field.ty.abiSize(target);1784 size += field.ty.abiSize(target);
1805 }1785 }
1786 size = std.mem.alignForwardGeneric(u64, size, big_align);
1806 return size;1787 return size;
1807 },1788 },
1808 .enum_simple, .enum_full, .enum_nonexhaustive, .enum_numbered => {1789 .enum_simple, .enum_full, .enum_nonexhaustive, .enum_numbered => {
...@@ -1810,9 +1791,9 @@ pub const Type = extern union {...@@ -1810,9 +1791,9 @@ pub const Type = extern union {
1810 const int_tag_ty = self.intTagType(&buffer);1791 const int_tag_ty = self.intTagType(&buffer);
1811 return int_tag_ty.abiSize(target);1792 return int_tag_ty.abiSize(target);
1812 },1793 },
1813 .@"union", .union_tagged => {1794 // TODO pass `true` for have_tag when unions have a safety tag
1814 @panic("TODO abiSize unions");1795 .@"union" => return self.castTag(.@"union").?.data.abiSize(target, false),
1815 },1796 .union_tagged => return self.castTag(.union_tagged).?.data.abiSize(target, true),
18161797
1817 .u1,1798 .u1,
1818 .u8,1799 .u8,
...@@ -2550,6 +2531,11 @@ pub const Type = extern union {...@@ -2550,6 +2531,11 @@ pub const Type = extern union {
2550 };2531 };
2551 }2532 }
25522533
2534 pub fn unionFields(ty: Type) Module.Union.Fields {
2535 const union_obj = ty.cast(Payload.Union).?.data;
2536 return union_obj.fields;
2537 }
2538
2553 pub fn unionFieldType(ty: Type, enum_tag: Value) Type {2539 pub fn unionFieldType(ty: Type, enum_tag: Value) Type {
2554 const union_obj = ty.cast(Payload.Union).?.data;2540 const union_obj = ty.cast(Payload.Union).?.data;
2555 const index = union_obj.tag_ty.enumTagFieldIndex(enum_tag).?;2541 const index = union_obj.tag_ty.enumTagFieldIndex(enum_tag).?;
...@@ -2657,7 +2643,7 @@ pub const Type = extern union {...@@ -2657,7 +2643,7 @@ pub const Type = extern union {
2657 };2643 };
2658 }2644 }
26592645
2660 /// Asserts the type is an integer or enum.2646 /// Asserts the type is an integer, enum, or error set.
2661 pub fn intInfo(self: Type, target: Target) struct { signedness: std.builtin.Signedness, bits: u16 } {2647 pub fn intInfo(self: Type, target: Target) struct { signedness: std.builtin.Signedness, bits: u16 } {
2662 var ty = self;2648 var ty = self;
2663 while (true) switch (ty.tag()) {2649 while (true) switch (ty.tag()) {
...@@ -2700,6 +2686,11 @@ pub const Type = extern union {...@@ -2700,6 +2686,11 @@ pub const Type = extern union {
2700 return .{ .signedness = .unsigned, .bits = smallestUnsignedBits(field_count - 1) };2686 return .{ .signedness = .unsigned, .bits = smallestUnsignedBits(field_count - 1) };
2701 },2687 },
27022688
2689 .error_set, .error_set_single, .anyerror, .error_set_inferred => {
2690 // TODO revisit this when error sets support custom int types
2691 return .{ .signedness = .unsigned, .bits = 16 };
2692 },
2693
2703 else => unreachable,2694 else => unreachable,
2704 };2695 };
2705 }2696 }
...@@ -3151,12 +3142,12 @@ pub const Type = extern union {...@@ -3151,12 +3142,12 @@ pub const Type = extern union {
31513142
3152 /// Asserts the type is an enum or a union.3143 /// Asserts the type is an enum or a union.
3153 /// TODO support unions3144 /// TODO support unions
3154 pub fn intTagType(self: Type, buffer: *Payload.Bits) Type {3145 pub fn intTagType(ty: Type, buffer: *Payload.Bits) Type {
3155 switch (self.tag()) {3146 switch (ty.tag()) {
3156 .enum_full, .enum_nonexhaustive => return self.cast(Payload.EnumFull).?.data.tag_ty,3147 .enum_full, .enum_nonexhaustive => return ty.cast(Payload.EnumFull).?.data.tag_ty,
3157 .enum_numbered => return self.castTag(.enum_numbered).?.data.tag_ty,3148 .enum_numbered => return ty.castTag(.enum_numbered).?.data.tag_ty,
3158 .enum_simple => {3149 .enum_simple => {
3159 const enum_simple = self.castTag(.enum_simple).?.data;3150 const enum_simple = ty.castTag(.enum_simple).?.data;
3160 const bits = std.math.log2_int_ceil(usize, enum_simple.fields.count());3151 const bits = std.math.log2_int_ceil(usize, enum_simple.fields.count());
3161 buffer.* = .{3152 buffer.* = .{
3162 .base = .{ .tag = .int_unsigned },3153 .base = .{ .tag = .int_unsigned },
...@@ -3164,6 +3155,7 @@ pub const Type = extern union {...@@ -3164,6 +3155,7 @@ pub const Type = extern union {
3164 };3155 };
3165 return Type.initPayload(&buffer.base);3156 return Type.initPayload(&buffer.base);
3166 },3157 },
3158 .union_tagged => return ty.castTag(.union_tagged).?.data.tag_ty.intTagType(buffer),
3167 else => unreachable,3159 else => unreachable,
3168 }3160 }
3169 }3161 }
...@@ -3317,6 +3309,16 @@ pub const Type = extern union {...@@ -3317,6 +3309,16 @@ pub const Type = extern union {
3317 }3309 }
3318 }3310 }
33193311
3312 pub fn structFields(ty: Type) Module.Struct.Fields {
3313 switch (ty.tag()) {
3314 .@"struct" => {
3315 const struct_obj = ty.castTag(.@"struct").?.data;
3316 return struct_obj.fields;
3317 },
3318 else => unreachable,
3319 }
3320 }
3321
3320 pub fn structFieldCount(ty: Type) usize {3322 pub fn structFieldCount(ty: Type) usize {
3321 switch (ty.tag()) {3323 switch (ty.tag()) {
3322 .@"struct" => {3324 .@"struct" => {
...@@ -3815,7 +3817,7 @@ pub const Type = extern union {...@@ -3815,7 +3817,7 @@ pub const Type = extern union {
3815 bit_offset: u16 = 0,3817 bit_offset: u16 = 0,
3816 host_size: u16 = 0,3818 host_size: u16 = 0,
3817 @"allowzero": bool = false,3819 @"allowzero": bool = false,
3818 mutable: bool = true, // TODO change this to const, not mutable3820 mutable: bool = true, // TODO rename this to const, not mutable
3819 @"volatile": bool = false,3821 @"volatile": bool = false,
3820 size: std.builtin.TypeInfo.Pointer.Size = .One,3822 size: std.builtin.TypeInfo.Pointer.Size = .One,
3821 };3823 };
test/behavior.zig+1-1
...@@ -15,7 +15,6 @@ test {...@@ -15,7 +15,6 @@ test {
15 _ = @import("behavior/bugs/4769_a.zig");15 _ = @import("behavior/bugs/4769_a.zig");
16 _ = @import("behavior/bugs/4769_b.zig");16 _ = @import("behavior/bugs/4769_b.zig");
17 _ = @import("behavior/bugs/6850.zig");17 _ = @import("behavior/bugs/6850.zig");
18 _ = @import("behavior/bugs/9584.zig");
19 _ = @import("behavior/call.zig");18 _ = @import("behavior/call.zig");
20 _ = @import("behavior/cast.zig");19 _ = @import("behavior/cast.zig");
21 _ = @import("behavior/defer.zig");20 _ = @import("behavior/defer.zig");
...@@ -104,6 +103,7 @@ test {...@@ -104,6 +103,7 @@ test {
104 _ = @import("behavior/bugs/7047.zig");103 _ = @import("behavior/bugs/7047.zig");
105 _ = @import("behavior/bugs/7003.zig");104 _ = @import("behavior/bugs/7003.zig");
106 _ = @import("behavior/bugs/7250.zig");105 _ = @import("behavior/bugs/7250.zig");
106 _ = @import("behavior/bugs/9584.zig");
107 _ = @import("behavior/byteswap.zig");107 _ = @import("behavior/byteswap.zig");
108 _ = @import("behavior/byval_arg_var.zig");108 _ = @import("behavior/byval_arg_var.zig");
109 _ = @import("behavior/call_stage1.zig");109 _ = @import("behavior/call_stage1.zig");
test/behavior/array.zig+26
...@@ -50,3 +50,29 @@ test "array literal with inferred length" {...@@ -50,3 +50,29 @@ test "array literal with inferred length" {
50 try expect(hex_mult.len == 4);50 try expect(hex_mult.len == 4);
51 try expect(hex_mult[1] == 256);51 try expect(hex_mult[1] == 256);
52}52}
53
54test "array dot len const expr" {
55 try expect(comptime x: {
56 break :x some_array.len == 4;
57 });
58}
59
60const ArrayDotLenConstExpr = struct {
61 y: [some_array.len]u8,
62};
63const some_array = [_]u8{ 0, 1, 2, 3 };
64
65test "array literal with specified size" {
66 var array = [2]u8{ 1, 2 };
67 try expect(array[0] == 1);
68 try expect(array[1] == 2);
69}
70
71test "array len field" {
72 var arr = [4]u8{ 0, 0, 0, 0 };
73 var ptr = &arr;
74 try expect(arr.len == 4);
75 comptime try expect(arr.len == 4);
76 try expect(ptr.len == 4);
77 comptime try expect(ptr.len == 4);
78}
test/behavior/array_stage1.zig-29
...@@ -39,17 +39,6 @@ test "void arrays" {...@@ -39,17 +39,6 @@ test "void arrays" {
39 try expect(array.len == 4);39 try expect(array.len == 4);
40}40}
4141
42test "array dot len const expr" {
43 try expect(comptime x: {
44 break :x some_array.len == 4;
45 });
46}
47
48const ArrayDotLenConstExpr = struct {
49 y: [some_array.len]u8,
50};
51const some_array = [_]u8{ 0, 1, 2, 3 };
52
53test "nested arrays" {42test "nested arrays" {
54 const array_of_strings = [_][]const u8{ "hello", "this", "is", "my", "thing" };43 const array_of_strings = [_][]const u8{ "hello", "this", "is", "my", "thing" };
55 for (array_of_strings) |s, i| {44 for (array_of_strings) |s, i| {
...@@ -76,24 +65,6 @@ test "set global var array via slice embedded in struct" {...@@ -76,24 +65,6 @@ test "set global var array via slice embedded in struct" {
76 try expect(s_array[2].b == 3);65 try expect(s_array[2].b == 3);
77}66}
7867
79test "array literal with specified size" {
80 var array = [2]u8{
81 1,
82 2,
83 };
84 try expect(array[0] == 1);
85 try expect(array[1] == 2);
86}
87
88test "array len field" {
89 var arr = [4]u8{ 0, 0, 0, 0 };
90 var ptr = &arr;
91 try expect(arr.len == 4);
92 comptime try expect(arr.len == 4);
93 try expect(ptr.len == 4);
94 comptime try expect(ptr.len == 4);
95}
96
97test "single-item pointer to array indexing and slicing" {68test "single-item pointer to array indexing and slicing" {
98 try testSingleItemPtrArrayIndexSlice();69 try testSingleItemPtrArrayIndexSlice();
99 comptime try testSingleItemPtrArrayIndexSlice();70 comptime try testSingleItemPtrArrayIndexSlice();
test/behavior/bugs/9584.zig+1
...@@ -57,4 +57,5 @@ test "bug 9584" {...@@ -57,4 +57,5 @@ test "bug 9584" {
57 .x = flags,57 .x = flags,
58 };58 };
59 try b(&x);59 try b(&x);
60 comptime if (@sizeOf(A) != 1) unreachable;
60}61}
test/behavior/struct.zig+8
...@@ -144,3 +144,11 @@ fn makeBar2(x: i32, y: i32) Bar {...@@ -144,3 +144,11 @@ fn makeBar2(x: i32, y: i32) Bar {
144 .y = y,144 .y = y,
145 };145 };
146}146}
147
148test "return empty struct from fn" {
149 _ = testReturnEmptyStructFromFn();
150}
151const EmptyStruct2 = struct {};
152fn testReturnEmptyStructFromFn() EmptyStruct2 {
153 return EmptyStruct2{};
154}
test/behavior/struct_stage1.zig-3
...@@ -72,9 +72,6 @@ const EmptyStruct = struct {...@@ -72,9 +72,6 @@ const EmptyStruct = struct {
72 }72 }
73};73};
7474
75test "return empty struct from fn" {
76 _ = testReturnEmptyStructFromFn();
77}
78const EmptyStruct2 = struct {};75const EmptyStruct2 = struct {};
79fn testReturnEmptyStructFromFn() EmptyStruct2 {76fn testReturnEmptyStructFromFn() EmptyStruct2 {
80 return EmptyStruct2{};77 return EmptyStruct2{};