| author | |
| committer | |
| log | 6d6cf598475ab8d2c3259002655ba04f1d056b2e |
| tree | daf38d2c3c68de0411d9106668bde92cc7e0651b |
| parent | f42725c39bbbe5db13c1a1706db3f31aa0549307 |
* 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 { |
| 644 | 662 | ||
| 645 | .const_ty => return Type.initTag(.type), | 663 | .const_ty => return Type.initTag(.type), |
| 646 | 664 | ||
| 647 | .alloc => return datas[inst].ty, | 665 | .alloc, |
| 666 | .ret_ptr, | ||
| 667 | => return datas[inst].ty, | ||
| 648 | 668 | ||
| 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), |
| 695 | 716 | ||
| ... | @@ -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 { |
| 714 | 735 | ||
| 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 | }, |
| 719 | 744 | ||
| 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( |
| 270 | 271 | ||
| 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, |
| 807 | 807 | ||
| 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 | }; |
| 960 | 962 | ||
| 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 { |
| 992 | 996 | ||
| 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 | else | 1005 | 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 | }; |
| 1011 | 1082 | ||
| 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 | } |
| 1819 | 1819 | ||
| 1820 | fn zirRef(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { | 1820 | fn 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; |
| 3333 | 3333 | ||
| 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 | }; | ||
| 3337 | 3348 | ||
| 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 we | 3630 | // 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 a | 3631 | // 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 | } |
| 3881 | 3897 | ||
| 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 | } |
| 4788 | 4808 | ||
| 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 | } |
| 4792 | 4817 | ||
| 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 |
| 8366 | 8391 | ||
| 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 | } |
| 8377 | 8404 | ||
| 8378 | fn analyzeRet( | 8405 | fn analyzeRet( |
| ... | @@ -8398,6 +8425,7 @@ fn analyzeRet( | ... | @@ -8398,6 +8425,7 @@ fn analyzeRet( |
| 8398 | return always_noreturn; | 8425 | return always_noreturn; |
| 8399 | } | 8426 | } |
| 8400 | 8427 | ||
| 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 | } |
| 8655 | 8683 | ||
| 8656 | fn zirArrayInit(sema: *Sema, block: *Block, inst: Zir.Inst.Index, is_ref: bool) CompileError!Air.Inst.Ref { | 8684 | fn 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(); |
| 8659 | 8693 | ||
| 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); | ||
| 8662 | 8700 | ||
| 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); |
| 8665 | 8702 | ||
| 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; | ||
| 8669 | 8715 | ||
| 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 | } |
| 8682 | 8726 | ||
| 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 | else | 8730 | 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 | }; | ||
| 8689 | 8735 | ||
| 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); |
| 8697 | 8743 | ||
| 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 | } |
| 8707 | 8755 | ||
| 8708 | fn zirArrayInitAnon(sema: *Sema, block: *Block, inst: Zir.Inst.Index, is_ref: bool) CompileError!Air.Inst.Ref { | 8756 | fn 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; |
| 10112 | 10160 | ||
| 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 branch | 10165 | // 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; |
| 10581 | 10630 | ||
| 10582 | const field_index = struct_obj.fields.getIndex(field_name) orelse | 10631 | 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]; |
| 10585 | 10635 | ||
| 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 | } |
| 10602 | 10652 | ||
| 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", .{}); |
| 11413 | 11459 | ||
| 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 comptime | 11475 | // TODO handle if the element type requires comptime |
| 11430 | 11476 | ||
| 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 | } |
| 11434 | 11481 |
src/arch/x86_64/abi.zig created+337| ... | @@ -0,0 +1,337 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const Type = @import("../../type.zig").Type; | ||
| 3 | const Target = std.Target; | ||
| 4 | const assert = std.debug.assert; | ||
| 5 | |||
| 6 | pub const Class = enum { integer, sse, sseup, x87, x87up, complex_x87, memory, none }; | ||
| 7 | |||
| 8 | pub 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. | ||
| 30 | pub 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), |
| 856 | 856 | ||
| 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), |
| 916 | 918 | ||
| 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 | } |
| 1187 | 1190 | ||
| 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 | } |
| 1559 | 1567 | ||
| 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; // TODO | 1577 | 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 | } |
| 3215 | 3231 | ||
| 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]"), |
| 1027 | 1021 | ||
| 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), | ||
| 1084 | 1081 | ||
| 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 | } |
| 1150 | 1147 | ||
| 1148 | fn 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 | |||
| 1151 | fn airAlloc(f: *Function, inst: Air.Inst.Index) !CValue { | 1164 | fn 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 | } |
| 1163 | 1176 | ||
| 1177 | fn 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 | |||
| 1164 | fn airArg(f: *Function) CValue { | 1189 | fn 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 | } |
| 1214 | 1239 | ||
| 1240 | fn 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 | |||
| 1215 | fn airIntCast(f: *Function, inst: Air.Inst.Index) !CValue { | 1255 | fn 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 | } |
| 1576 | 1621 | ||
| 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.data | 1624 | 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_decl | 1626 | break :fn_decl switch (callee_val.tag()) { |
| 1582 | else | 1627 | .extern_fn => callee_val.castTag(.extern_fn).?.data, |
| 1583 | unreachable; | 1628 | .function => callee_val.castTag(.function).?.data.owner_decl, |
| 1584 | 1629 | .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; |
| 21 | 21 | ||
| 22 | const LazySrcLoc = Module.LazySrcLoc; | 22 | const LazySrcLoc = Module.LazySrcLoc; |
| 23 | 23 | ||
| 24 | const Error = error{ OutOfMemory, CodegenFail }; | ||
| 25 | |||
| 24 | pub fn targetTriple(allocator: *Allocator, target: std.Target) ![:0]u8 { | 26 | pub 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 { |
| 410 | 412 | ||
| 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(); | ||
| 414 | 420 | ||
| 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 | } |
| 418 | 428 | ||
| 419 | // Remove all the basic blocks of a function in order to start over, generating | 429 | // 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, |
| 558 | 569 | ||
| 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 | }; |
| 592 | 603 | ||
| 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 | } |
| 597 | 608 | ||
| 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 has | 610 | /// 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.*; |
| 604 | 615 | ||
| 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(); |
| 609 | 620 | 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 | } | ||
| 620 | 621 | ||
| 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); | ||
| 625 | 624 | ||
| 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"); | ||
| 633 | 626 | ||
| 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); |
| 636 | 629 | ||
| 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; |
| 639 | 633 | ||
| 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 | } |
| 645 | 639 | ||
| 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 generate | 659 | // 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 the | 661 | // 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 stack | 662 | // 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 | } |
| 654 | 666 | ||
| 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 | } |
| 662 | 673 | ||
| 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 attributes | 689 | // 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 | } |
| 682 | 693 | ||
| 683 | return llvm_fn; | 694 | return llvm_fn; |
| 684 | } | 695 | } |
| 685 | 696 | ||
| 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 | } |
| 694 | 711 | ||
| 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 | } |
| 710 | 727 | ||
| 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); |
| 739 | 756 | ||
| 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); |
| 760 | 777 | ||
| 761 | if (t.isPtrLikeOptional()) { | 778 | if (t.isPtrLikeOptional()) { |
| 762 | return payload_llvm_ty; | 779 | return payload_llvm_ty; |
| 763 | } | 780 | } |
| 764 | 781 | ||
| 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); |
| 778 | 795 | ||
| 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.*; |
| 788 | 805 | ||
| 789 | // The Type memory is ephemeral; since we want to store a longer-lived | 806 | // 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); |
| 792 | 809 | ||
| 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); |
| 798 | 815 | ||
| 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 calls | 817 | gop.value_ptr.* = llvm_struct_ty; // must be done before any recursive calls |
| 801 | 818 | ||
| 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 { |
| 805 | 822 | ||
| 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 | } |
| 810 | 827 | ||
| 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()); |
| 822 | 839 | ||
| 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); |
| 825 | 842 | ||
| 826 | if (union_obj.onlyTagHasCodegenBits()) { | 843 | if (union_obj.onlyTagHasCodegenBits()) { |
| 827 | return enum_tag_llvm_ty; | 844 | return enum_tag_llvm_ty; |
| 828 | } | 845 | } |
| 829 | 846 | ||
| 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 the | 850 | // TODO handle when the most aligned field is different than the |
| 834 | // biggest sized field. | 851 | // biggest sized field. |
| 835 | 852 | ||
| 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 | } |
| 877 | 908 | ||
| 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); |
| 1057 | 1097 | ||
| 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 | } |
| 1155 | 1194 | ||
| 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 | } |
| 1159 | 1198 | ||
| 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 | } |
| 1165 | 1208 | ||
| 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 | } |
| 1178 | 1221 | ||
| 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 | } |
| 1182 | 1225 | ||
| ... | @@ -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 | } |
| 1186 | 1229 | ||
| 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 | } |
| 1190 | 1233 | ||
| ... | @@ -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), |
| 1229 | 1272 | ||
| 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 parameters | 1275 | /// 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, |
| 1234 | 1281 | ||
| ... | @@ -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 | } |
| 1260 | 1307 | ||
| 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 { |
| 1269 | 1316 | ||
| 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 | } |
| 1277 | 1336 | ||
| 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), |
| 1321 | 1380 | ||
| 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), |
| 1372 | 1433 | ||
| 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); | ||
| 1412 | 1482 | ||
| 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 | }; | ||
| 1415 | 1492 | ||
| 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 | } |
| 1419 | 1499 | ||
| 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 | ); |
| 1428 | 1508 | ||
| 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 | } |
| 1438 | 1521 | ||
| 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 | } |
| 1449 | 1544 | ||
| 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"); |
| 1493 | 1602 | ||
| 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 | } | ||
| 1507 | 1615 | ||
| 1508 | try self.genBody(body); | 1616 | try self.genBody(body); |
| 1509 | 1617 | ||
| ... | @@ -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; |
| 1516 | 1624 | ||
| 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 | } |
| 1658 | 1777 | ||
| 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 | } |
| 1672 | 1790 | ||
| 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); |
| 1681 | 1798 | ||
| ... | @@ -1686,18 +1803,35 @@ pub const FuncGen = struct { | ... | @@ -1686,18 +1803,35 @@ pub const FuncGen = struct { |
| 1686 | 1803 | ||
| 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 | } |
| 1691 | 1808 | ||
| 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; // TODO | 1810 | if (self.liveness.isUnused(inst)) return null; |
| 1694 | if (!is_volatile and self.liveness.isUnused(inst)) | ||
| 1695 | return null; | ||
| 1696 | 1811 | ||
| 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 | } |
| 1710 | 1844 | ||
| 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 | } |
| 1728 | 1862 | ||
| 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 | } |
| 1742 | 1875 | ||
| 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 | } |
| 1777 | 1920 | ||
| 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; |
| 2466 | 2609 | ||
| 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 | } |
| 2472 | 2621 | ||
| 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 | } |
| 2486 | 2633 | ||
| 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 to | 2647 | /// 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 | } |
| 2519 | 2679 | ||
| ... | @@ -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 truncating | 2819 | // 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); |
| 2681 | 2842 | ||
| 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 | } |
| 2695 | 2855 | ||
| ... | @@ -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 | } |
| 2845 | 3004 | ||
| 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 | }; |
| 2867 | 3048 | ||
| ... | @@ -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 | |||
| 3298 | fn 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 | |||
| 3314 | fn 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 { |
| 163 | 163 | ||
| 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 | }; |
| 167 | 179 | ||
| 168 | pub const Type = opaque { | 180 | pub const Type = opaque { |
| ... | @@ -292,12 +304,6 @@ pub const VerifierFailureAction = enum(c_int) { | ... | @@ -292,12 +304,6 @@ pub const VerifierFailureAction = enum(c_int) { |
| 292 | pub const constNeg = LLVMConstNeg; | 304 | pub const constNeg = LLVMConstNeg; |
| 293 | extern fn LLVMConstNeg(ConstantVal: *const Value) *const Value; | 305 | extern fn LLVMConstNeg(ConstantVal: *const Value) *const Value; |
| 294 | 306 | ||
| 295 | pub const setInitializer = LLVMSetInitializer; | ||
| 296 | extern fn LLVMSetInitializer(GlobalVar: *const Value, ConstantVal: *const Value) void; | ||
| 297 | |||
| 298 | pub const getParam = LLVMGetParam; | ||
| 299 | extern fn LLVMGetParam(Fn: *const Value, Index: c_uint) *const Value; | ||
| 300 | |||
| 301 | pub const getEnumAttributeKindForName = LLVMGetEnumAttributeKindForName; | 307 | pub const getEnumAttributeKindForName = LLVMGetEnumAttributeKindForName; |
| 302 | extern fn LLVMGetEnumAttributeKindForName(Name: [*]const u8, SLen: usize) c_uint; | 308 | extern fn LLVMGetEnumAttributeKindForName(Name: [*]const u8, SLen: usize) c_uint; |
| 303 | 309 |
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), |
| 154 | 156 | ||
| 155 | .breakpoint, | 157 | .breakpoint, |
| ... | @@ -158,6 +160,7 @@ const Writer = struct { | ... | @@ -158,6 +160,7 @@ const Writer = struct { |
| 158 | 160 | ||
| 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), |
| 162 | 165 | ||
| 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; |
| 1795 | 1774 | ||
| ... | @@ -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), |
| 1816 | 1797 | ||
| 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 | } |
| 2552 | 2533 | ||
| 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 | } |
| 2659 | 2645 | ||
| 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 | }, |
| 2702 | 2688 | ||
| 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 { |
| 3151 | 3142 | ||
| 3152 | /// Asserts the type is an enum or a union. | 3143 | /// Asserts the type is an enum or a union. |
| 3153 | /// TODO support unions | 3144 | /// 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 | } |
| 3319 | 3311 | ||
| 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 mutable | 3820 | 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 | |||
| 54 | test "array dot len const expr" { | ||
| 55 | try expect(comptime x: { | ||
| 56 | break :x some_array.len == 4; | ||
| 57 | }); | ||
| 58 | } | ||
| 59 | |||
| 60 | const ArrayDotLenConstExpr = struct { | ||
| 61 | y: [some_array.len]u8, | ||
| 62 | }; | ||
| 63 | const some_array = [_]u8{ 0, 1, 2, 3 }; | ||
| 64 | |||
| 65 | test "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 | |||
| 71 | test "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 | } |
| 41 | 41 | ||
| 42 | test "array dot len const expr" { | ||
| 43 | try expect(comptime x: { | ||
| 44 | break :x some_array.len == 4; | ||
| 45 | }); | ||
| 46 | } | ||
| 47 | |||
| 48 | const ArrayDotLenConstExpr = struct { | ||
| 49 | y: [some_array.len]u8, | ||
| 50 | }; | ||
| 51 | const some_array = [_]u8{ 0, 1, 2, 3 }; | ||
| 52 | |||
| 53 | test "nested arrays" { | 42 | test "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 | } |
| 78 | 67 | ||
| 79 | test "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 | |||
| 88 | test "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 | |||
| 97 | test "single-item pointer to array indexing and slicing" { | 68 | test "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 | |||
| 148 | test "return empty struct from fn" { | ||
| 149 | _ = testReturnEmptyStructFromFn(); | ||
| 150 | } | ||
| 151 | const EmptyStruct2 = struct {}; | ||
| 152 | fn 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 | }; |
| 74 | 74 | ||
| 75 | test "return empty struct from fn" { | ||
| 76 | _ = testReturnEmptyStructFromFn(); | ||
| 77 | } | ||
| 78 | const EmptyStruct2 = struct {}; | 75 | const EmptyStruct2 = struct {}; |
| 79 | fn testReturnEmptyStructFromFn() EmptyStruct2 { | 76 | fn testReturnEmptyStructFromFn() EmptyStruct2 { |
| 80 | return EmptyStruct2{}; | 77 | return EmptyStruct2{}; |