| 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 | 110 | /// Allocates stack local memory. |
| 111 | 111 | /// Uses the `ty` field. |
| 112 | 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 | 117 | /// Inline assembly. Uses the `ty_pl` field. Payload is `Asm`. |
| 114 | 118 | assembly, |
| 115 | 119 | /// Bitwise AND. `&`. |
| ... | ... | @@ -160,6 +164,7 @@ pub const Inst = struct { |
| 160 | 164 | /// Function call. |
| 161 | 165 | /// Result type is the return type of the function being called. |
| 162 | 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 | 168 | call, |
| 164 | 169 | /// Count leading zeroes of an integer according to its representation in twos complement. |
| 165 | 170 | /// Result type will always be an unsigned integer big enough to fit the answer. |
| ... | ... | @@ -257,7 +262,16 @@ pub const Inst = struct { |
| 257 | 262 | /// Return a value from a function. |
| 258 | 263 | /// Result type is always noreturn; no instructions in a block follow this one. |
| 259 | 264 | /// Uses the `un_op` field. |
| 265 | /// Triggers `resolveTypeLayout` on the return type. | |
| 260 | 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 | 275 | /// Write a value to a pointer. LHS is pointer, RHS is value. |
| 262 | 276 | /// Result type is always void. |
| 263 | 277 | /// Uses the `bin_op` field. |
| ... | ... | @@ -341,6 +355,10 @@ pub const Inst = struct { |
| 341 | 355 | /// Given a slice value, return the pointer. |
| 342 | 356 | /// Uses the `ty_op` field. |
| 343 | 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 | 362 | /// Given a slice value, and element index, return the element value at that index. |
| 345 | 363 | /// Result type is the element type of the slice operand. |
| 346 | 364 | /// Uses the `bin_op` field. |
| ... | ... | @@ -644,7 +662,9 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { |
| 644 | 662 | |
| 645 | 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 | 669 | .assembly, |
| 650 | 670 | .block, |
| ... | ... | @@ -690,6 +710,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { |
| 690 | 710 | .cond_br, |
| 691 | 711 | .switch_br, |
| 692 | 712 | .ret, |
| 713 | .ret_load, | |
| 693 | 714 | .unreach, |
| 694 | 715 | => return Type.initTag(.noreturn), |
| 695 | 716 | |
| ... | ... | @@ -714,10 +735,14 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { |
| 714 | 735 | |
| 715 | 736 | .call => { |
| 716 | 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 | 746 | const ptr_ty = air.typeOf(datas[inst].bin_op.lhs); |
| 722 | 747 | return ptr_ty.elemType(); |
| 723 | 748 | }, |
src/Liveness.zig+3| ... | ... | @@ -250,6 +250,7 @@ fn analyzeInst( |
| 250 | 250 | .bool_and, |
| 251 | 251 | .bool_or, |
| 252 | 252 | .store, |
| 253 | .array_elem_val, | |
| 253 | 254 | .slice_elem_val, |
| 254 | 255 | .ptr_slice_elem_val, |
| 255 | 256 | .ptr_elem_val, |
| ... | ... | @@ -270,6 +271,7 @@ fn analyzeInst( |
| 270 | 271 | |
| 271 | 272 | .arg, |
| 272 | 273 | .alloc, |
| 274 | .ret_ptr, | |
| 273 | 275 | .constant, |
| 274 | 276 | .const_ty, |
| 275 | 277 | .breakpoint, |
| ... | ... | @@ -322,6 +324,7 @@ fn analyzeInst( |
| 322 | 324 | .ptrtoint, |
| 323 | 325 | .bool_to_int, |
| 324 | 326 | .ret, |
| 327 | .ret_load, | |
| 325 | 328 | => { |
| 326 | 329 | const operand = inst_datas[inst].un_op; |
| 327 | 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 | 785 | /// The Decl that corresponds to the struct itself. |
| 786 | 786 | owner_decl: *Decl, |
| 787 | 787 | /// Set of field names in declaration order. |
| 788 | fields: std.StringArrayHashMapUnmanaged(Field), | |
| 788 | fields: Fields, | |
| 789 | 789 | /// Represents the declarations inside this struct. |
| 790 | 790 | namespace: Namespace, |
| 791 | 791 | /// Offset from `owner_decl`, points to the struct AST node. |
| ... | ... | @@ -805,6 +805,8 @@ pub const Struct = struct { |
| 805 | 805 | /// is necessary to determine whether it has bits at runtime. |
| 806 | 806 | known_has_bits: bool, |
| 807 | 807 | |
| 808 | pub const Fields = std.StringArrayHashMapUnmanaged(Field); | |
| 809 | ||
| 808 | 810 | /// The `Type` and `Value` memory is owned by the arena of the Struct's owner_decl. |
| 809 | 811 | pub const Field = struct { |
| 810 | 812 | /// Uses `noreturn` to indicate `anytype`. |
| ... | ... | @@ -935,7 +937,7 @@ pub const Union = struct { |
| 935 | 937 | /// This will be set to the null type until status is `have_field_types`. |
| 936 | 938 | tag_ty: Type, |
| 937 | 939 | /// Set of field names in declaration order. |
| 938 | fields: std.StringArrayHashMapUnmanaged(Field), | |
| 940 | fields: Fields, | |
| 939 | 941 | /// Represents the declarations inside this union. |
| 940 | 942 | namespace: Namespace, |
| 941 | 943 | /// Offset from `owner_decl`, points to the union decl AST node. |
| ... | ... | @@ -958,6 +960,8 @@ pub const Union = struct { |
| 958 | 960 | abi_align: Value, |
| 959 | 961 | }; |
| 960 | 962 | |
| 963 | pub const Fields = std.StringArrayHashMapUnmanaged(Field); | |
| 964 | ||
| 961 | 965 | pub fn getFullyQualifiedName(s: *Union, gpa: *Allocator) ![]u8 { |
| 962 | 966 | return s.owner_decl.getFullyQualifiedName(gpa); |
| 963 | 967 | } |
| ... | ... | @@ -992,14 +996,18 @@ pub const Union = struct { |
| 992 | 996 | |
| 993 | 997 | pub fn mostAlignedField(u: Union, target: Target) u32 { |
| 994 | 998 | assert(u.haveFieldTypes()); |
| 995 | var most_alignment: u64 = 0; | |
| 999 | var most_alignment: u32 = 0; | |
| 996 | 1000 | var most_index: usize = undefined; |
| 997 | 1001 | for (u.fields.values()) |field, i| { |
| 998 | 1002 | if (!field.ty.hasCodeGenBits()) continue; |
| 999 | const field_align = if (field.abi_align.tag() == .abi_align_default) | |
| 1000 | field.ty.abiAlignment(target) | |
| 1001 | else | |
| 1002 | field.abi_align.toUnsignedInt(); | |
| 1003 | ||
| 1004 | const field_align = a: { | |
| 1005 | if (field.abi_align.tag() == .abi_align_default) { | |
| 1006 | break :a field.ty.abiAlignment(target); | |
| 1007 | } else { | |
| 1008 | break :a @intCast(u32, field.abi_align.toUnsignedInt()); | |
| 1009 | } | |
| 1010 | }; | |
| 1003 | 1011 | if (field_align > most_alignment) { |
| 1004 | 1012 | most_alignment = field_align; |
| 1005 | 1013 | most_index = i; |
| ... | ... | @@ -1007,6 +1015,69 @@ pub const Union = struct { |
| 1007 | 1015 | } |
| 1008 | 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 | 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 | 1814 | .pointee_type = sema.fn_ret_ty, |
| 1815 | 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 | 1820 | fn zirRef(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| ... | ... | @@ -3331,9 +3331,20 @@ fn analyzeCall( |
| 3331 | 3331 | ) CompileError!Air.Inst.Ref { |
| 3332 | 3332 | const mod = sema.mod; |
| 3333 | 3333 | |
| 3334 | const func_ty = sema.typeOf(func); | |
| 3335 | if (func_ty.zigTypeTag() != .Fn) | |
| 3336 | return sema.fail(block, func_src, "type '{}' not a function", .{func_ty}); | |
| 3334 | const callee_ty = sema.typeOf(func); | |
| 3335 | const func_ty = 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 | 3349 | const func_ty_info = func_ty.fnInfo(); |
| 3339 | 3350 | const cc = func_ty_info.cc; |
| ... | ... | @@ -3393,6 +3404,7 @@ fn analyzeCall( |
| 3393 | 3404 | const result: Air.Inst.Ref = if (is_inline_call) res: { |
| 3394 | 3405 | const func_val = try sema.resolveConstValue(block, func_src, func); |
| 3395 | 3406 | const module_fn = switch (func_val.tag()) { |
| 3407 | .decl_ref => func_val.castTag(.decl_ref).?.data.val.castTag(.function).?.data, | |
| 3396 | 3408 | .function => func_val.castTag(.function).?.data, |
| 3397 | 3409 | .extern_fn => return sema.fail(block, call_src, "{s} call of extern function", .{ |
| 3398 | 3410 | @as([]const u8, if (is_comptime_call) "comptime" else "inline"), |
| ... | ... | @@ -3610,7 +3622,11 @@ fn analyzeCall( |
| 3610 | 3622 | break :res res2; |
| 3611 | 3623 | } else if (func_ty_info.is_generic) res: { |
| 3612 | 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 | 3630 | // Check the Module's generic function map with an adapted context, so that we |
| 3615 | 3631 | // can match against `uncasted_args` rather than doing the work below to create a |
| 3616 | 3632 | // generic Scope only to junk it if it matches an existing instantiation. |
| ... | ... | @@ -3880,6 +3896,8 @@ fn analyzeCall( |
| 3880 | 3896 | } |
| 3881 | 3897 | |
| 3882 | 3898 | try sema.requireRuntimeBlock(block, call_src); |
| 3899 | try sema.resolveTypeLayout(block, call_src, func_ty_info.return_type); | |
| 3900 | ||
| 3883 | 3901 | try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Call).Struct.fields.len + |
| 3884 | 3902 | args.len); |
| 3885 | 3903 | const func_inst = try block.addInst(.{ |
| ... | ... | @@ -3954,6 +3972,8 @@ fn finishGenericCall( |
| 3954 | 3972 | } |
| 3955 | 3973 | total_i += 1; |
| 3956 | 3974 | } |
| 3975 | ||
| 3976 | try sema.resolveTypeLayout(block, call_src, new_fn_ty.fnReturnType()); | |
| 3957 | 3977 | } |
| 3958 | 3978 | try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.Call).Struct.fields.len + |
| 3959 | 3979 | runtime_args_len); |
| ... | ... | @@ -4787,7 +4807,12 @@ fn funcCommon( |
| 4787 | 4807 | } |
| 4788 | 4808 | |
| 4789 | 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 | 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 | 8391 | |
| 8367 | 8392 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; |
| 8368 | 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 | 8394 | const ret_ptr = sema.resolveInst(inst_data.operand); |
| 8374 | const operand = try sema.analyzeLoad(block, src, ret_ptr, src); | |
| 8375 | return sema.analyzeRet(block, operand, src, false); | |
| 8395 | ||
| 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 | 8405 | fn analyzeRet( |
| ... | ... | @@ -8398,6 +8425,7 @@ fn analyzeRet( |
| 8398 | 8425 | return always_noreturn; |
| 8399 | 8426 | } |
| 8400 | 8427 | |
| 8428 | try sema.resolveTypeLayout(block, src, sema.fn_ret_ty); | |
| 8401 | 8429 | _ = try block.addUnOp(.ret, operand); |
| 8402 | 8430 | return always_noreturn; |
| 8403 | 8431 | } |
| ... | ... | @@ -8653,56 +8681,76 @@ fn zirStructInitAnon(sema: *Sema, block: *Block, inst: Zir.Inst.Index, is_ref: b |
| 8653 | 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 | 8691 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; |
| 8658 | 8692 | const src = inst_data.src(); |
| 8659 | 8693 | |
| 8660 | 8694 | const extra = sema.code.extraData(Zir.Inst.MultiOp, inst_data.payload_index); |
| 8661 | 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 | 8701 | for (args) |arg, i| resolved_args[i] = sema.resolveInst(arg); |
| 8665 | 8702 | |
| 8666 | var all_args_comptime = for (resolved_args) |arg| { | |
| 8667 | if ((try sema.resolveMaybeUndefVal(block, src, arg)) == null) break false; | |
| 8668 | } else true; | |
| 8703 | const elem_ty = sema.typeOf(resolved_args[0]); | |
| 8704 | ||
| 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 | 8717 | var anon_decl = try block.startAnonDecl(); |
| 8672 | 8718 | defer anon_decl.deinit(); |
| 8673 | assert(!(resolved_args.len == 0)); | |
| 8674 | const final_ty = try Type.Tag.array.create(anon_decl.arena(), .{ | |
| 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); | |
| 8719 | ||
| 8720 | const elem_vals = try anon_decl.arena().alloc(Value, resolved_args.len); | |
| 8679 | 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); | |
| 8684 | if (is_ref) | |
| 8685 | return sema.analyzeDeclRef(try anon_decl.finish(final_ty, val)) | |
| 8686 | else | |
| 8687 | return sema.analyzeDeclVal(block, .unneeded, try anon_decl.finish(final_ty, val)); | |
| 8688 | } | |
| 8727 | const val = try Value.Tag.array.create(anon_decl.arena(), elem_vals); | |
| 8728 | const decl = try anon_decl.finish(try array_ty.copy(anon_decl.arena()), val); | |
| 8729 | if (is_ref) { | |
| 8730 | return sema.analyzeDeclRef(decl); | |
| 8731 | } else { | |
| 8732 | return sema.analyzeDeclVal(block, .unneeded, decl); | |
| 8733 | } | |
| 8734 | }; | |
| 8689 | 8735 | |
| 8690 | assert(!(resolved_args.len == 0)); | |
| 8691 | const array_ty = try Type.Tag.array.create(sema.arena, .{ .len = resolved_args.len, .elem_type = sema.typeOf(resolved_args[0]) }); | |
| 8692 | const final_ty = try Type.ptr(sema.arena, .{ | |
| 8736 | try sema.requireRuntimeBlock(block, runtime_src); | |
| 8737 | ||
| 8738 | const alloc_ty = try Type.ptr(sema.arena, .{ | |
| 8693 | 8739 | .pointee_type = array_ty, |
| 8694 | 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 | 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)); | |
| 8700 | _ = try block.addBinOp(.store, pointer_to_array_at_index, arg); | |
| 8745 | const index = try sema.addIntUnsigned(Type.initTag(.u64), i); | |
| 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 | 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 | 10159 | const arena = sema.arena; |
| 10112 | 10160 | |
| 10113 | 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 | 10164 | if (!this_feature_is_implemented_in_the_backend) { |
| 10116 | 10165 | // TODO implement this feature in all the backends and then delete this branch |
| 10117 | 10166 | _ = try block.addNoOp(.breakpoint); |
| ... | ... | @@ -10579,8 +10628,9 @@ fn fieldCallBind( |
| 10579 | 10628 | const struct_ty = try sema.resolveTypeFields(block, src, concrete_ty); |
| 10580 | 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 | 10632 | break :find_field; |
| 10633 | const field_index = @intCast(u32, field_index_usize); | |
| 10584 | 10634 | const field = struct_obj.fields.values()[field_index]; |
| 10585 | 10635 | |
| 10586 | 10636 | const ptr_field_ty = try Type.ptr(arena, .{ |
| ... | ... | @@ -10601,33 +10651,7 @@ fn fieldCallBind( |
| 10601 | 10651 | } |
| 10602 | 10652 | |
| 10603 | 10653 | try sema.requireRuntimeBlock(block, src); |
| 10604 | const ptr_inst = ptr_inst: { | |
| 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 | }; | |
| 10654 | const ptr_inst = try block.addStructFieldPtr(object_ptr, field_index, ptr_field_ty); | |
| 10631 | 10655 | return sema.analyzeLoad(block, src, ptr_inst, src); |
| 10632 | 10656 | }, |
| 10633 | 10657 | .Union => return sema.fail(block, src, "TODO implement field calls on unions", .{}), |
| ... | ... | @@ -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 | 11023 | else => return sema.fail( |
| 10986 | 11024 | block, |
| 10987 | 11025 | array_ptr_src, |
| 10988 | "expected pointer, found '{}'", | |
| 11026 | "expected pointer or array; found '{}'", | |
| 10989 | 11027 | .{maybe_ptr_ty}, |
| 10990 | 11028 | ), |
| 10991 | 11029 | } |
| ... | ... | @@ -11085,6 +11123,14 @@ fn coerce( |
| 11085 | 11123 | return sema.wrapOptional(block, dest_type, intermediate, inst_src); |
| 11086 | 11124 | }, |
| 11087 | 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 | 11134 | // Coercions where the source is a single pointer to an array. |
| 11089 | 11135 | src_array_ptr: { |
| 11090 | 11136 | if (!inst_ty.isSinglePointer()) break :src_array_ptr; |
| ... | ... | @@ -11411,7 +11457,7 @@ fn storePtr2( |
| 11411 | 11457 | if (ptr_ty.isConstPtr()) |
| 11412 | 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 | 11461 | const operand = try sema.coerce(block, elem_ty, uncasted_operand, operand_src); |
| 11416 | 11462 | if ((try sema.typeHasOnePossibleValue(block, src, elem_ty)) != null) |
| 11417 | 11463 | return; |
| ... | ... | @@ -11429,6 +11475,7 @@ fn storePtr2( |
| 11429 | 11475 | // TODO handle if the element type requires comptime |
| 11430 | 11476 | |
| 11431 | 11477 | try sema.requireRuntimeBlock(block, runtime_src); |
| 11478 | try sema.resolveTypeLayout(block, src, elem_ty); | |
| 11432 | 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 | 855 | .shr => try self.airShr(inst), |
| 856 | 856 | |
| 857 | 857 | .alloc => try self.airAlloc(inst), |
| 858 | .ret_ptr => try self.airRetPtr(inst), | |
| 858 | 859 | .arg => try self.airArg(inst), |
| 859 | 860 | .assembly => try self.airAsm(inst), |
| 860 | 861 | .bitcast => try self.airBitCast(inst), |
| ... | ... | @@ -883,6 +884,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 883 | 884 | .not => try self.airNot(inst), |
| 884 | 885 | .ptrtoint => try self.airPtrToInt(inst), |
| 885 | 886 | .ret => try self.airRet(inst), |
| 887 | .ret_load => try self.airRetLoad(inst), | |
| 886 | 888 | .store => try self.airStore(inst), |
| 887 | 889 | .struct_field_ptr=> try self.airStructFieldPtr(inst), |
| 888 | 890 | .struct_field_val=> try self.airStructFieldVal(inst), |
| ... | ... | @@ -914,6 +916,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 914 | 916 | .slice_ptr => try self.airSlicePtr(inst), |
| 915 | 917 | .slice_len => try self.airSliceLen(inst), |
| 916 | 918 | |
| 919 | .array_elem_val => try self.airArrayElemVal(inst), | |
| 917 | 920 | .slice_elem_val => try self.airSliceElemVal(inst), |
| 918 | 921 | .ptr_slice_elem_val => try self.airPtrSliceElemVal(inst), |
| 919 | 922 | .ptr_elem_val => try self.airPtrElemVal(inst), |
| ... | ... | @@ -1185,6 +1188,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1185 | 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 | 1196 | fn airFptrunc(self: *Self, inst: Air.Inst.Index) !void { |
| 1189 | 1197 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 1190 | 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 | 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 | 1576 | fn airPtrSliceElemVal(self: *Self, inst: Air.Inst.Index) !void { |
| 1561 | 1577 | const is_volatile = false; // TODO |
| 1562 | 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 | 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 | 3240 | fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { |
| 3217 | 3241 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 3218 | 3242 | if (self.liveness.isUnused(inst)) |
src/codegen/c.zig+67-17| ... | ... | @@ -384,12 +384,6 @@ pub const DeclGen = struct { |
| 384 | 384 | } |
| 385 | 385 | }, |
| 386 | 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 | 387 | .function => { |
| 394 | 388 | const decl = val.castTag(.function).?.data.owner_decl; |
| 395 | 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 | 1020 | .is_non_null_ptr => try airIsNull(f, inst, "!=", "[0]"), |
| 1027 | 1021 | |
| 1028 | 1022 | .alloc => try airAlloc(f, inst), |
| 1023 | .ret_ptr => try airRetPtr(f, inst), | |
| 1029 | 1024 | .assembly => try airAsm(f, inst), |
| 1030 | 1025 | .block => try airBlock(f, inst), |
| 1031 | 1026 | .bitcast => try airBitcast(f, inst), |
| ... | ... | @@ -1036,6 +1031,7 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO |
| 1036 | 1031 | .bool_to_int => try airBoolToInt(f, inst), |
| 1037 | 1032 | .load => try airLoad(f, inst), |
| 1038 | 1033 | .ret => try airRet(f, inst), |
| 1034 | .ret_load => try airRetLoad(f, inst), | |
| 1039 | 1035 | .store => try airStore(f, inst), |
| 1040 | 1036 | .loop => try airLoop(f, inst), |
| 1041 | 1037 | .cond_br => try airCondBr(f, inst), |
| ... | ... | @@ -1081,6 +1077,7 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO |
| 1081 | 1077 | .ptr_elem_ptr => try airPtrElemPtr(f, inst), |
| 1082 | 1078 | .slice_elem_val => try airSliceElemVal(f, inst, "["), |
| 1083 | 1079 | .ptr_slice_elem_val => try airSliceElemVal(f, inst, "[0]["), |
| 1080 | .array_elem_val => try airArrayElemVal(f, inst), | |
| 1084 | 1081 | |
| 1085 | 1082 | .unwrap_errunion_payload => try airUnwrapErrUnionPay(f, inst), |
| 1086 | 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 | 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 | 1164 | fn airAlloc(f: *Function, inst: Air.Inst.Index) !CValue { |
| 1152 | 1165 | const writer = f.object.writer(); |
| 1153 | 1166 | const inst_ty = f.air.typeOfIndex(inst); |
| ... | ... | @@ -1161,6 +1174,18 @@ fn airAlloc(f: *Function, inst: Air.Inst.Index) !CValue { |
| 1161 | 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 | 1189 | fn airArg(f: *Function) CValue { |
| 1165 | 1190 | const i = f.next_arg_index; |
| 1166 | 1191 | f.next_arg_index += 1; |
| ... | ... | @@ -1212,6 +1237,21 @@ fn airRet(f: *Function, inst: Air.Inst.Index) !CValue { |
| 1212 | 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 | 1255 | fn airIntCast(f: *Function, inst: Air.Inst.Index) !CValue { |
| 1216 | 1256 | if (f.liveness.isUnused(inst)) |
| 1217 | 1257 | return CValue.none; |
| ... | ... | @@ -1559,7 +1599,12 @@ fn airCall(f: *Function, inst: Air.Inst.Index) !CValue { |
| 1559 | 1599 | const pl_op = f.air.instructions.items(.data)[inst].pl_op; |
| 1560 | 1600 | const extra = f.air.extraData(Air.Call, pl_op.payload); |
| 1561 | 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 | 1608 | const ret_ty = fn_ty.fnReturnType(); |
| 1564 | 1609 | const unused_result = f.liveness.isUnused(inst); |
| 1565 | 1610 | const writer = f.object.writer(); |
| ... | ... | @@ -1574,16 +1619,21 @@ fn airCall(f: *Function, inst: Air.Inst.Index) !CValue { |
| 1574 | 1619 | try writer.writeAll(" = "); |
| 1575 | 1620 | } |
| 1576 | 1621 | |
| 1577 | if (f.air.value(pl_op.operand)) |func_val| { | |
| 1578 | const fn_decl = if (func_val.castTag(.extern_fn)) |extern_fn| | |
| 1579 | extern_fn.data | |
| 1580 | else if (func_val.castTag(.function)) |func_payload| | |
| 1581 | func_payload.data.owner_decl | |
| 1582 | else | |
| 1583 | unreachable; | |
| 1584 | ||
| 1585 | try f.object.dg.renderDeclName(fn_decl, writer); | |
| 1586 | } else { | |
| 1622 | callee: { | |
| 1623 | known: { | |
| 1624 | const fn_decl = fn_decl: { | |
| 1625 | const callee_val = f.air.value(pl_op.operand) orelse break :known; | |
| 1626 | break :fn_decl switch (callee_val.tag()) { | |
| 1627 | .extern_fn => callee_val.castTag(.extern_fn).?.data, | |
| 1628 | .function => callee_val.castTag(.function).?.data.owner_decl, | |
| 1629 | .decl_ref => callee_val.castTag(.decl_ref).?.data, | |
| 1630 | else => break :known, | |
| 1631 | }; | |
| 1632 | }; | |
| 1633 | try f.object.dg.renderDeclName(fn_decl, writer); | |
| 1634 | break :callee; | |
| 1635 | } | |
| 1636 | // Fall back to function pointer call. | |
| 1587 | 1637 | const callee = try f.resolveInst(pl_op.operand); |
| 1588 | 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 | 21 | |
| 22 | 22 | const LazySrcLoc = Module.LazySrcLoc; |
| 23 | 23 | |
| 24 | const Error = error{ OutOfMemory, CodegenFail }; | |
| 25 | ||
| 24 | 26 | pub fn targetTriple(allocator: *Allocator, target: std.Target) ![:0]u8 { |
| 25 | 27 | const llvm_arch = switch (target.cpu.arch) { |
| 26 | 28 | .arm => "arm", |
| ... | ... | @@ -410,10 +412,18 @@ pub const Object = struct { |
| 410 | 412 | |
| 411 | 413 | // This gets the LLVM values from the function and stores them in `dg.args`. |
| 412 | 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| { | |
| 416 | arg.* = llvm.getParam(llvm_func, @intCast(c_uint, i)); | |
| 421 | const param_offset: c_uint = @boolToInt(ret_ptr != null); | |
| 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 | 429 | // Remove all the basic blocks of a function in order to start over, generating |
| ... | ... | @@ -434,7 +444,8 @@ pub const Object = struct { |
| 434 | 444 | .context = dg.context, |
| 435 | 445 | .dg = &dg, |
| 436 | 446 | .builder = builder, |
| 437 | .args = args, | |
| 447 | .ret_ptr = ret_ptr, | |
| 448 | .args = args.toOwnedSlice(), | |
| 438 | 449 | .arg_index = 0, |
| 439 | 450 | .func_inst_table = .{}, |
| 440 | 451 | .entry_block = entry_block, |
| ... | ... | @@ -556,7 +567,7 @@ pub const DeclGen = struct { |
| 556 | 567 | gpa: *Allocator, |
| 557 | 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 | 571 | @setCold(true); |
| 561 | 572 | assert(self.err_msg == null); |
| 562 | 573 | const src_loc = @as(LazySrcLoc, .{ .node_offset = 0 }).toSrcLoc(self.decl); |
| ... | ... | @@ -591,50 +602,33 @@ pub const DeclGen = struct { |
| 591 | 602 | }; |
| 592 | 603 | |
| 593 | 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 | 609 | /// If the llvm function does not exist, create it. |
| 599 | 610 | /// Note that this can be called before the function's semantic analysis has |
| 600 | 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 { | |
| 602 | const gop = try self.object.decl_map.getOrPut(self.gpa, decl); | |
| 612 | fn resolveLlvmFunction(dg: *DeclGen, decl: *Module.Decl) !*const llvm.Value { | |
| 613 | const gop = try dg.object.decl_map.getOrPut(dg.gpa, decl); | |
| 603 | 614 | if (gop.found_existing) return gop.value_ptr.*; |
| 604 | 615 | |
| 605 | 616 | assert(decl.has_tv); |
| 606 | 617 | const zig_fn_type = decl.ty; |
| 607 | 618 | const fn_info = zig_fn_type.fnInfo(); |
| 608 | const return_type = fn_info.return_type; | |
| 609 | ||
| 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 | } | |
| 619 | const target = dg.module.getTarget(); | |
| 620 | const sret = firstParamSRet(fn_info, target); | |
| 620 | 621 | |
| 621 | const llvm_ret_ty = if (!return_type.hasCodeGenBits()) | |
| 622 | self.context.voidType() | |
| 623 | else | |
| 624 | try self.llvmType(return_type); | |
| 622 | const return_type = fn_info.return_type; | |
| 623 | const raw_llvm_ret_ty = try dg.llvmType(return_type); | |
| 625 | 624 | |
| 626 | const fn_type = llvm.functionType( | |
| 627 | llvm_ret_ty, | |
| 628 | llvm_param_buffer.ptr, | |
| 629 | llvm_params_len, | |
| 630 | .False, | |
| 631 | ); | |
| 632 | const llvm_addrspace = self.llvmAddressSpace(decl.@"addrspace"); | |
| 625 | const fn_type = try dg.llvmType(zig_fn_type); | |
| 633 | 626 | |
| 634 | const fqn = try decl.getFullyQualifiedName(self.gpa); | |
| 635 | defer self.gpa.free(fqn); | |
| 627 | const fqn = try decl.getFullyQualifiedName(dg.gpa); | |
| 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 | 632 | gop.value_ptr.* = llvm_fn; |
| 639 | 633 | |
| 640 | 634 | const is_extern = decl.val.tag() == .extern_fn; |
| ... | ... | @@ -643,53 +637,76 @@ pub const DeclGen = struct { |
| 643 | 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 | 659 | // The intent here is for compiler-rt and libc functions to not generate |
| 648 | 660 | // infinite recursion. For example, if we are compiling the memcpy function, |
| 649 | 661 | // and llvm detects that the body is equivalent to memcpy, it may replace the |
| 650 | 662 | // body of memcpy with a call to memcpy, which would then cause a stack |
| 651 | 663 | // overflow instead of performing memcpy. |
| 652 | self.addFnAttr(llvm_fn, "nobuiltin"); | |
| 664 | dg.addFnAttr(llvm_fn, "nobuiltin"); | |
| 653 | 665 | } |
| 654 | 666 | |
| 655 | 667 | // TODO: more attributes. see codegen.cpp `make_fn_llvm_value`. |
| 656 | const target = self.module.getTarget(); | |
| 657 | 668 | if (fn_info.cc == .Naked) { |
| 658 | self.addFnAttr(llvm_fn, "naked"); | |
| 669 | dg.addFnAttr(llvm_fn, "naked"); | |
| 659 | 670 | } else { |
| 660 | 671 | llvm_fn.setFunctionCallConv(toLlvmCallConv(fn_info.cc, target)); |
| 661 | 672 | } |
| 662 | 673 | |
| 663 | 674 | // Function attributes that are independent of analysis results of the function body. |
| 664 | if (!self.module.comp.bin_file.options.red_zone) { | |
| 665 | self.addFnAttr(llvm_fn, "noredzone"); | |
| 675 | if (!dg.module.comp.bin_file.options.red_zone) { | |
| 676 | dg.addFnAttr(llvm_fn, "noredzone"); | |
| 666 | 677 | } |
| 667 | self.addFnAttr(llvm_fn, "nounwind"); | |
| 668 | if (self.module.comp.unwind_tables) { | |
| 669 | self.addFnAttr(llvm_fn, "uwtable"); | |
| 678 | dg.addFnAttr(llvm_fn, "nounwind"); | |
| 679 | if (dg.module.comp.unwind_tables) { | |
| 680 | dg.addFnAttr(llvm_fn, "uwtable"); | |
| 670 | 681 | } |
| 671 | if (self.module.comp.bin_file.options.optimize_mode == .ReleaseSmall) { | |
| 672 | self.addFnAttr(llvm_fn, "minsize"); | |
| 673 | self.addFnAttr(llvm_fn, "optsize"); | |
| 682 | if (dg.module.comp.bin_file.options.optimize_mode == .ReleaseSmall) { | |
| 683 | dg.addFnAttr(llvm_fn, "minsize"); | |
| 684 | dg.addFnAttr(llvm_fn, "optsize"); | |
| 674 | 685 | } |
| 675 | if (self.module.comp.bin_file.options.tsan) { | |
| 676 | self.addFnAttr(llvm_fn, "sanitize_thread"); | |
| 686 | if (dg.module.comp.bin_file.options.tsan) { | |
| 687 | dg.addFnAttr(llvm_fn, "sanitize_thread"); | |
| 677 | 688 | } |
| 678 | 689 | // TODO add target-cpu and target-features fn attributes |
| 679 | 690 | if (return_type.isNoReturn()) { |
| 680 | self.addFnAttr(llvm_fn, "noreturn"); | |
| 691 | dg.addFnAttr(llvm_fn, "noreturn"); | |
| 681 | 692 | } |
| 682 | 693 | |
| 683 | 694 | return llvm_fn; |
| 684 | 695 | } |
| 685 | 696 | |
| 686 | fn resolveGlobalDecl(self: *DeclGen, decl: *Module.Decl) error{ OutOfMemory, CodegenFail }!*const llvm.Value { | |
| 687 | const llvm_module = self.object.llvm_module; | |
| 688 | if (llvm_module.getNamedGlobal(decl.name)) |val| return val; | |
| 689 | // TODO: remove this redundant `llvmType`, it is also called in `genTypedValue`. | |
| 690 | const llvm_type = try self.llvmType(decl.ty); | |
| 691 | const llvm_addrspace = self.llvmAddressSpace(decl.@"addrspace"); | |
| 692 | return llvm_module.addGlobalInAddressSpace(llvm_type, decl.name, llvm_addrspace); | |
| 697 | fn resolveGlobalDecl(dg: *DeclGen, decl: *Module.Decl) Error!*const llvm.Value { | |
| 698 | const gop = try dg.object.decl_map.getOrPut(dg.gpa, decl); | |
| 699 | if (gop.found_existing) return gop.value_ptr.*; | |
| 700 | errdefer assert(dg.object.decl_map.remove(decl)); | |
| 701 | ||
| 702 | const fqn = try decl.getFullyQualifiedName(dg.gpa); | |
| 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 | 712 | fn llvmAddressSpace(self: DeclGen, address_space: std.builtin.AddressSpace) c_uint { |
| ... | ... | @@ -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 { | |
| 712 | const gpa = self.gpa; | |
| 728 | fn llvmType(dg: *DeclGen, t: Type) Error!*const llvm.Type { | |
| 729 | const gpa = dg.gpa; | |
| 713 | 730 | log.debug("llvmType for {}", .{t}); |
| 714 | 731 | switch (t.zigTypeTag()) { |
| 715 | .Void, .NoReturn => return self.context.voidType(), | |
| 732 | .Void, .NoReturn => return dg.context.voidType(), | |
| 716 | 733 | .Int => { |
| 717 | const info = t.intInfo(self.module.getTarget()); | |
| 718 | return self.context.intType(info.bits); | |
| 734 | const info = t.intInfo(dg.module.getTarget()); | |
| 735 | return dg.context.intType(info.bits); | |
| 719 | 736 | }, |
| 720 | 737 | .Enum => { |
| 721 | 738 | var buffer: Type.Payload.Bits = undefined; |
| 722 | 739 | const int_ty = t.intTagType(&buffer); |
| 723 | const bit_count = int_ty.intInfo(self.module.getTarget()).bits; | |
| 724 | return self.context.intType(bit_count); | |
| 740 | const bit_count = int_ty.intInfo(dg.module.getTarget()).bits; | |
| 741 | return dg.context.intType(bit_count); | |
| 725 | 742 | }, |
| 726 | .Float => switch (t.floatBits(self.module.getTarget())) { | |
| 727 | 16 => return self.context.halfType(), | |
| 728 | 32 => return self.context.floatType(), | |
| 729 | 64 => return self.context.doubleType(), | |
| 730 | 80 => return self.context.x86FP80Type(), | |
| 731 | 128 => return self.context.fp128Type(), | |
| 743 | .Float => switch (t.floatBits(dg.module.getTarget())) { | |
| 744 | 16 => return dg.context.halfType(), | |
| 745 | 32 => return dg.context.floatType(), | |
| 746 | 64 => return dg.context.doubleType(), | |
| 747 | 80 => return dg.context.x86FP80Type(), | |
| 748 | 128 => return dg.context.fp128Type(), | |
| 732 | 749 | else => unreachable, |
| 733 | 750 | }, |
| 734 | .Bool => return self.context.intType(1), | |
| 751 | .Bool => return dg.context.intType(1), | |
| 735 | 752 | .Pointer => { |
| 736 | 753 | if (t.isSlice()) { |
| 737 | 754 | var buf: Type.SlicePtrFieldTypeBuffer = undefined; |
| 738 | 755 | const ptr_type = t.slicePtrFieldType(&buf); |
| 739 | 756 | |
| 740 | 757 | const fields: [2]*const llvm.Type = .{ |
| 741 | try self.llvmType(ptr_type), | |
| 742 | try self.llvmType(Type.initTag(.usize)), | |
| 758 | try dg.llvmType(ptr_type), | |
| 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 | 762 | } else { |
| 746 | const elem_type = try self.llvmType(t.elemType()); | |
| 747 | const llvm_addrspace = self.llvmAddressSpace(t.ptrAddressSpace()); | |
| 763 | const elem_type = try dg.llvmType(t.elemType()); | |
| 764 | const llvm_addrspace = dg.llvmAddressSpace(t.ptrAddressSpace()); | |
| 748 | 765 | return elem_type.pointerType(llvm_addrspace); |
| 749 | 766 | } |
| 750 | 767 | }, |
| 751 | 768 | .Array => { |
| 752 | const elem_type = try self.llvmType(t.elemType()); | |
| 769 | const elem_type = try dg.llvmType(t.elemType()); | |
| 753 | 770 | const total_len = t.arrayLen() + @boolToInt(t.sentinel() != null); |
| 754 | 771 | return elem_type.arrayType(@intCast(c_uint, total_len)); |
| 755 | 772 | }, |
| 756 | 773 | .Optional => { |
| 757 | 774 | var buf: Type.Payload.ElemType = undefined; |
| 758 | 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 | 778 | if (t.isPtrLikeOptional()) { |
| 762 | 779 | return payload_llvm_ty; |
| 763 | 780 | } |
| 764 | 781 | |
| 765 | 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 | 787 | .ErrorUnion => { |
| 771 | 788 | const error_type = t.errorUnionSet(); |
| 772 | 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 | 791 | if (!payload_type.hasCodeGenBits()) { |
| 775 | 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 | 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 | 799 | .ErrorSet => { |
| 783 | return self.context.intType(16); | |
| 800 | return dg.context.intType(16); | |
| 784 | 801 | }, |
| 785 | 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 | 804 | if (gop.found_existing) return gop.value_ptr.*; |
| 788 | 805 | |
| 789 | 806 | // The Type memory is ephemeral; since we want to store a longer-lived |
| 790 | 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 | 810 | const struct_obj = t.castTag(.@"struct").?.data; |
| 794 | 811 | assert(struct_obj.haveFieldTypes()); |
| ... | ... | @@ -796,7 +813,7 @@ pub const DeclGen = struct { |
| 796 | 813 | const name = try struct_obj.getFullyQualifiedName(gpa); |
| 797 | 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 | 817 | gop.value_ptr.* = llvm_struct_ty; // must be done before any recursive calls |
| 801 | 818 | |
| 802 | 819 | var llvm_field_types: std.ArrayListUnmanaged(*const llvm.Type) = .{}; |
| ... | ... | @@ -805,7 +822,7 @@ pub const DeclGen = struct { |
| 805 | 822 | |
| 806 | 823 | for (struct_obj.fields.values()) |field| { |
| 807 | 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 | 828 | llvm_struct_ty.structSetBody( |
| ... | ... | @@ -821,42 +838,56 @@ pub const DeclGen = struct { |
| 821 | 838 | assert(union_obj.haveFieldTypes()); |
| 822 | 839 | |
| 823 | 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 | 843 | if (union_obj.onlyTagHasCodegenBits()) { |
| 827 | 844 | return enum_tag_llvm_ty; |
| 828 | 845 | } |
| 829 | 846 | |
| 830 | const target = self.module.getTarget(); | |
| 847 | const target = dg.module.getTarget(); | |
| 831 | 848 | const most_aligned_field_index = union_obj.mostAlignedField(target); |
| 832 | 849 | const most_aligned_field = union_obj.fields.values()[most_aligned_field_index]; |
| 833 | 850 | // TODO handle when the most aligned field is different than the |
| 834 | 851 | // biggest sized field. |
| 835 | 852 | |
| 836 | 853 | const llvm_fields = [_]*const llvm.Type{ |
| 837 | try self.llvmType(most_aligned_field.ty), | |
| 854 | try dg.llvmType(most_aligned_field.ty), | |
| 838 | 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 | 859 | .Fn => { |
| 843 | const ret_ty = try self.llvmType(t.fnReturnType()); | |
| 844 | const params_len = t.fnParamLen(); | |
| 845 | const llvm_params = try gpa.alloc(*const llvm.Type, params_len); | |
| 846 | defer gpa.free(llvm_params); | |
| 847 | for (llvm_params) |*llvm_param, i| { | |
| 848 | llvm_param.* = try self.llvmType(t.fnParamType(i)); | |
| 860 | const fn_info = t.fnInfo(); | |
| 861 | const target = dg.module.getTarget(); | |
| 862 | const sret = firstParamSRet(fn_info, target); | |
| 863 | const return_type = fn_info.return_type; | |
| 864 | const raw_llvm_ret_ty = try dg.llvmType(return_type); | |
| 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(); | |
| 851 | const llvm_fn_ty = llvm.functionType( | |
| 852 | ret_ty, | |
| 853 | llvm_params.ptr, | |
| 854 | @intCast(c_uint, llvm_params.len), | |
| 855 | llvm.Bool.fromBool(is_var_args), | |
| 884 | ||
| 885 | return llvm.functionType( | |
| 886 | llvm_ret_ty, | |
| 887 | llvm_params.items.ptr, | |
| 888 | @intCast(c_uint, llvm_params.items.len), | |
| 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 | 892 | .ComptimeInt => unreachable, |
| 862 | 893 | .ComptimeFloat => unreachable, |
| ... | ... | @@ -871,11 +902,11 @@ pub const DeclGen = struct { |
| 871 | 902 | .Frame, |
| 872 | 903 | .AnyFrame, |
| 873 | 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 | 910 | if (tv.val.isUndef()) { |
| 880 | 911 | const llvm_type = try self.llvmType(tv.ty); |
| 881 | 912 | return llvm_type.getUndef(); |
| ... | ... | @@ -961,9 +992,12 @@ pub const DeclGen = struct { |
| 961 | 992 | } else { |
| 962 | 993 | const decl = tv.val.castTag(.decl_ref).?.data; |
| 963 | 994 | decl.alive = true; |
| 964 | const val = try self.resolveGlobalDecl(decl); | |
| 965 | 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 | 1003 | .variable => { |
| ... | ... | @@ -1047,17 +1081,23 @@ pub const DeclGen = struct { |
| 1047 | 1081 | return self.todo("handle more array values", .{}); |
| 1048 | 1082 | }, |
| 1049 | 1083 | .Optional => { |
| 1084 | var buf: Type.Payload.ElemType = undefined; | |
| 1085 | const payload_ty = tv.ty.optionalChild(&buf); | |
| 1086 | ||
| 1050 | 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 | 1095 | const is_pl = !tv.val.isNull(); |
| 1056 | 1096 | const llvm_i1 = self.context.intType(1); |
| 1057 | 1097 | |
| 1058 | 1098 | const fields: [2]*const llvm.Value = .{ |
| 1059 | 1099 | try self.genTypedValue(.{ |
| 1060 | .ty = payload_type, | |
| 1100 | .ty = payload_ty, | |
| 1061 | 1101 | .val = if (tv.val.castTag(.opt_payload)) |pl| pl.data else Value.initTag(.undef), |
| 1062 | 1102 | }), |
| 1063 | 1103 | if (is_pl) llvm_i1.constAllOnes() else llvm_i1.constNull(), |
| ... | ... | @@ -1068,7 +1108,6 @@ pub const DeclGen = struct { |
| 1068 | 1108 | const fn_decl = switch (tv.val.tag()) { |
| 1069 | 1109 | .extern_fn => tv.val.castTag(.extern_fn).?.data, |
| 1070 | 1110 | .function => tv.val.castTag(.function).?.data.owner_decl, |
| 1071 | .decl_ref => tv.val.castTag(.decl_ref).?.data, | |
| 1072 | 1111 | else => unreachable, |
| 1073 | 1112 | }; |
| 1074 | 1113 | fn_decl.alive = true; |
| ... | ... | @@ -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 | 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 | 1203 | fn removeAttr(val: *const llvm.Value, index: llvm.AttributeIndex, name: []const u8) void { |
| 1161 | 1204 | const kind_id = llvm.getEnumAttributeKindForName(name.ptr, name.len); |
| 1162 | 1205 | assert(kind_id != 0); |
| ... | ... | @@ -1164,7 +1207,7 @@ pub const DeclGen = struct { |
| 1164 | 1207 | } |
| 1165 | 1208 | |
| 1166 | 1209 | fn addAttrInt( |
| 1167 | dg: *DeclGen, | |
| 1210 | dg: DeclGen, | |
| 1168 | 1211 | val: *const llvm.Value, |
| 1169 | 1212 | index: llvm.AttributeIndex, |
| 1170 | 1213 | name: []const u8, |
| ... | ... | @@ -1176,7 +1219,7 @@ pub const DeclGen = struct { |
| 1176 | 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 | 1223 | dg.addAttr(val, std.math.maxInt(llvm.AttributeIndex), name); |
| 1181 | 1224 | } |
| 1182 | 1225 | |
| ... | ... | @@ -1184,7 +1227,7 @@ pub const DeclGen = struct { |
| 1184 | 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 | 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 | 1270 | /// in other instructions. This table is cleared before every function is generated. |
| 1228 | 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 | 1275 | /// These fields are used to refer to the LLVM value of the function parameters |
| 1231 | 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 | 1279 | args: []*const llvm.Value, |
| 1233 | 1280 | arg_index: usize, |
| 1234 | 1281 | |
| ... | ... | @@ -1258,7 +1305,7 @@ pub const FuncGen = struct { |
| 1258 | 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 | 1309 | @setCold(true); |
| 1263 | 1310 | return self.dg.todo(format, args); |
| 1264 | 1311 | } |
| ... | ... | @@ -1269,13 +1316,25 @@ pub const FuncGen = struct { |
| 1269 | 1316 | |
| 1270 | 1317 | fn resolveInst(self: *FuncGen, inst: Air.Inst.Ref) !*const llvm.Value { |
| 1271 | 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 | 1333 | const inst_index = Air.refToIndex(inst).?; |
| 1275 | 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 | 1338 | const air_tags = self.air.instructions.items(.tag); |
| 1280 | 1339 | for (body) |inst| { |
| 1281 | 1340 | const opt_value: ?*const llvm.Value = switch (air_tags[inst]) { |
| ... | ... | @@ -1320,6 +1379,7 @@ pub const FuncGen = struct { |
| 1320 | 1379 | .is_err_ptr => try self.airIsErr(inst, .NE, true), |
| 1321 | 1380 | |
| 1322 | 1381 | .alloc => try self.airAlloc(inst), |
| 1382 | .ret_ptr => try self.airRetPtr(inst), | |
| 1323 | 1383 | .arg => try self.airArg(inst), |
| 1324 | 1384 | .bitcast => try self.airBitCast(inst), |
| 1325 | 1385 | .bool_to_int => try self.airBoolToInt(inst), |
| ... | ... | @@ -1338,6 +1398,7 @@ pub const FuncGen = struct { |
| 1338 | 1398 | .loop => try self.airLoop(inst), |
| 1339 | 1399 | .not => try self.airNot(inst), |
| 1340 | 1400 | .ret => try self.airRet(inst), |
| 1401 | .ret_load => try self.airRetLoad(inst), | |
| 1341 | 1402 | .store => try self.airStore(inst), |
| 1342 | 1403 | .assembly => try self.airAssembly(inst), |
| 1343 | 1404 | .slice_ptr => try self.airSliceField(inst, 0), |
| ... | ... | @@ -1370,6 +1431,7 @@ pub const FuncGen = struct { |
| 1370 | 1431 | .struct_field_ptr_index_2 => try self.airStructFieldPtrIndex(inst, 2), |
| 1371 | 1432 | .struct_field_ptr_index_3 => try self.airStructFieldPtrIndex(inst, 3), |
| 1372 | 1433 | |
| 1434 | .array_elem_val => try self.airArrayElemVal(inst), | |
| 1373 | 1435 | .slice_elem_val => try self.airSliceElemVal(inst), |
| 1374 | 1436 | .ptr_slice_elem_val => try self.airPtrSliceElemVal(inst), |
| 1375 | 1437 | .ptr_elem_val => try self.airPtrElemVal(inst), |
| ... | ... | @@ -1405,40 +1467,73 @@ pub const FuncGen = struct { |
| 1405 | 1467 | const pl_op = self.air.instructions.items(.data)[inst].pl_op; |
| 1406 | 1468 | const extra = self.air.extraData(Air.Call, pl_op.payload); |
| 1407 | 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); | |
| 1409 | const return_type = zig_fn_type.fnReturnType(); | |
| 1470 | const callee_ty = self.air.typeOf(pl_op.operand); | |
| 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 | 1479 | const llvm_fn = try self.resolveInst(pl_op.operand); |
| 1411 | 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); | |
| 1414 | defer self.gpa.free(llvm_param_vals); | |
| 1483 | var llvm_args = std.ArrayList(*const llvm.Value).init(self.gpa); | |
| 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 | 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 | 1500 | const call = self.builder.buildCall( |
| 1421 | 1501 | llvm_fn, |
| 1422 | llvm_param_vals.ptr, | |
| 1423 | @intCast(c_uint, args.len), | |
| 1424 | toLlvmCallConv(zig_fn_type.fnCallingConvention(), target), | |
| 1502 | llvm_args.items.ptr, | |
| 1503 | @intCast(c_uint, llvm_args.items.len), | |
| 1504 | toLlvmCallConv(zig_fn_ty.fnCallingConvention(), target), | |
| 1425 | 1505 | .Auto, |
| 1426 | 1506 | "", |
| 1427 | 1507 | ); |
| 1428 | 1508 | |
| 1429 | 1509 | if (return_type.isNoReturn()) { |
| 1430 | 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 | 1522 | fn airRet(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { |
| 1440 | 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 | 1537 | _ = self.builder.buildRetVoid(); |
| 1443 | 1538 | return null; |
| 1444 | 1539 | } |
| ... | ... | @@ -1447,6 +1542,20 @@ pub const FuncGen = struct { |
| 1447 | 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 | 1559 | fn airCmp(self: *FuncGen, inst: Air.Inst.Index, op: math.CompareOperator) !?*const llvm.Value { |
| 1451 | 1560 | if (self.liveness.isUnused(inst)) |
| 1452 | 1561 | return null; |
| ... | ... | @@ -1491,19 +1600,18 @@ pub const FuncGen = struct { |
| 1491 | 1600 | const body = self.air.extra[extra.end..][0..extra.data.body_len]; |
| 1492 | 1601 | const parent_bb = self.context.createBasicBlock("Block"); |
| 1493 | 1602 | |
| 1494 | // 5 breaks to a block seems like a reasonable default. | |
| 1495 | var break_bbs = try BreakBasicBlocks.initCapacity(self.gpa, 5); | |
| 1496 | var break_vals = try BreakValues.initCapacity(self.gpa, 5); | |
| 1603 | var break_bbs: BreakBasicBlocks = .{}; | |
| 1604 | defer break_bbs.deinit(self.gpa); | |
| 1605 | ||
| 1606 | var break_vals: BreakValues = .{}; | |
| 1607 | defer break_vals.deinit(self.gpa); | |
| 1608 | ||
| 1497 | 1609 | try self.blocks.putNoClobber(self.gpa, inst, .{ |
| 1498 | 1610 | .parent_bb = parent_bb, |
| 1499 | 1611 | .break_bbs = &break_bbs, |
| 1500 | 1612 | .break_vals = &break_vals, |
| 1501 | 1613 | }); |
| 1502 | defer { | |
| 1503 | assert(self.blocks.remove(inst)); | |
| 1504 | break_bbs.deinit(self.gpa); | |
| 1505 | break_vals.deinit(self.gpa); | |
| 1506 | } | |
| 1614 | defer assert(self.blocks.remove(inst)); | |
| 1507 | 1615 | |
| 1508 | 1616 | try self.genBody(body); |
| 1509 | 1617 | |
| ... | ... | @@ -1514,7 +1622,18 @@ pub const FuncGen = struct { |
| 1514 | 1622 | const inst_ty = self.air.typeOfIndex(inst); |
| 1515 | 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 | 1637 | phi_node.addIncoming( |
| 1519 | 1638 | break_vals.items.ptr, |
| 1520 | 1639 | break_bbs.items.ptr, |
| ... | ... | @@ -1657,25 +1776,23 @@ pub const FuncGen = struct { |
| 1657 | 1776 | } |
| 1658 | 1777 | |
| 1659 | 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 | 1779 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 1665 | const lhs = try self.resolveInst(bin_op.lhs); | |
| 1666 | const rhs = try self.resolveInst(bin_op.rhs); | |
| 1667 | const base_ptr = self.builder.buildExtractValue(lhs, 0, ""); | |
| 1668 | const indices: [1]*const llvm.Value = .{rhs}; | |
| 1780 | const slice_ty = self.air.typeOf(bin_op.lhs); | |
| 1781 | if (!slice_ty.isVolatilePtr() and self.liveness.isUnused(inst)) return null; | |
| 1782 | ||
| 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 | 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 | 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 | 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 | 1796 | const lhs = try self.resolveInst(bin_op.lhs); |
| 1680 | 1797 | const rhs = try self.resolveInst(bin_op.rhs); |
| 1681 | 1798 | |
| ... | ... | @@ -1686,18 +1803,35 @@ pub const FuncGen = struct { |
| 1686 | 1803 | |
| 1687 | 1804 | const indices: [1]*const llvm.Value = .{rhs}; |
| 1688 | 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 { | |
| 1693 | const is_volatile = false; // TODO | |
| 1694 | if (!is_volatile and self.liveness.isUnused(inst)) | |
| 1695 | return null; | |
| 1809 | fn airArrayElemVal(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { | |
| 1810 | if (self.liveness.isUnused(inst)) return null; | |
| 1696 | 1811 | |
| 1697 | 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 | 1832 | const base_ptr = try self.resolveInst(bin_op.lhs); |
| 1699 | 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 | 1835 | // If this is a single-item pointer to an array, we need another index in the GEP. |
| 1702 | 1836 | const indices: [2]*const llvm.Value = .{ self.context.intType(32).constNull(), rhs }; |
| 1703 | 1837 | break :ptr self.builder.buildInBoundsGEP(base_ptr, &indices, indices.len, ""); |
| ... | ... | @@ -1705,7 +1839,7 @@ pub const FuncGen = struct { |
| 1705 | 1839 | const indices: [1]*const llvm.Value = .{rhs}; |
| 1706 | 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 | 1845 | fn airPtrElemPtr(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { |
| ... | ... | @@ -1727,17 +1861,16 @@ pub const FuncGen = struct { |
| 1727 | 1861 | } |
| 1728 | 1862 | |
| 1729 | 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 | 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 | 1868 | const lhs = try self.resolveInst(bin_op.lhs); |
| 1736 | 1869 | const rhs = try self.resolveInst(bin_op.rhs); |
| 1737 | 1870 | const base_ptr = self.builder.buildLoad(lhs, ""); |
| 1738 | 1871 | const indices: [1]*const llvm.Value = .{rhs}; |
| 1739 | 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 | 1876 | fn airStructFieldPtr(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { |
| ... | ... | @@ -1770,9 +1903,19 @@ pub const FuncGen = struct { |
| 1770 | 1903 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 1771 | 1904 | const struct_field = self.air.extraData(Air.StructField, ty_pl.payload).data; |
| 1772 | 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 | 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 | 1921 | fn airNot(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { |
| ... | ... | @@ -2465,17 +2608,21 @@ pub const FuncGen = struct { |
| 2465 | 2608 | self.arg_index += 1; |
| 2466 | 2609 | |
| 2467 | 2610 | const inst_ty = self.air.typeOfIndex(inst); |
| 2468 | const ptr_val = self.buildAlloca(try self.dg.llvmType(inst_ty)); | |
| 2469 | _ = self.builder.buildStore(arg_val, ptr_val); | |
| 2470 | return self.builder.buildLoad(ptr_val, ""); | |
| 2611 | if (isByRef(inst_ty)) { | |
| 2612 | // TODO declare debug variable | |
| 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 | 2622 | fn airAlloc(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { |
| 2474 | 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 | 2624 | const ptr_ty = self.air.typeOfIndex(inst); |
| 2478 | const pointee_type = ptr_ty.elemType(); | |
| 2625 | const pointee_type = ptr_ty.childType(); | |
| 2479 | 2626 | if (!pointee_type.hasCodeGenBits()) return null; |
| 2480 | 2627 | const pointee_llvm_ty = try self.dg.llvmType(pointee_type); |
| 2481 | 2628 | const target = self.dg.module.getTarget(); |
| ... | ... | @@ -2484,6 +2631,19 @@ pub const FuncGen = struct { |
| 2484 | 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 | 2647 | /// Use this instead of builder.buildAlloca, because this function makes sure to |
| 2488 | 2648 | /// put the alloca instruction at the top of the function! |
| 2489 | 2649 | fn buildAlloca(self: *FuncGen, t: *const llvm.Type) *const llvm.Value { |
| ... | ... | @@ -2513,7 +2673,7 @@ pub const FuncGen = struct { |
| 2513 | 2673 | const dest_ptr = try self.resolveInst(bin_op.lhs); |
| 2514 | 2674 | const ptr_ty = self.air.typeOf(bin_op.lhs); |
| 2515 | 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 | 2677 | return null; |
| 2518 | 2678 | } |
| 2519 | 2679 | |
| ... | ... | @@ -2658,11 +2818,11 @@ pub const FuncGen = struct { |
| 2658 | 2818 | if (opt_abi_ty) |abi_ty| { |
| 2659 | 2819 | // operand needs widening and truncating |
| 2660 | 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 | 2822 | load_inst.setOrdering(ordering); |
| 2663 | 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 | 2826 | load_inst.setOrdering(ordering); |
| 2667 | 2827 | return load_inst; |
| 2668 | 2828 | } |
| ... | ... | @@ -2673,10 +2833,11 @@ pub const FuncGen = struct { |
| 2673 | 2833 | ordering: llvm.AtomicOrdering, |
| 2674 | 2834 | ) !?*const llvm.Value { |
| 2675 | 2835 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 2676 | var ptr = try self.resolveInst(bin_op.lhs); | |
| 2677 | 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 | 2840 | var element = try self.resolveInst(bin_op.rhs); |
| 2679 | const operand_ty = ptr_ty.elemType(); | |
| 2680 | 2841 | const opt_abi_ty = self.dg.getAtomicAbiType(operand_ty, false); |
| 2681 | 2842 | |
| 2682 | 2843 | if (opt_abi_ty) |abi_ty| { |
| ... | ... | @@ -2688,8 +2849,7 @@ pub const FuncGen = struct { |
| 2688 | 2849 | element = self.builder.buildZExt(element, abi_ty, ""); |
| 2689 | 2850 | } |
| 2690 | 2851 | } |
| 2691 | const store_inst = self.store(ptr, ptr_ty, element); | |
| 2692 | store_inst.setOrdering(ordering); | |
| 2852 | self.store(ptr, ptr_ty, element, ordering); | |
| 2693 | 2853 | return null; |
| 2694 | 2854 | } |
| 2695 | 2855 | |
| ... | ... | @@ -2724,10 +2884,9 @@ pub const FuncGen = struct { |
| 2724 | 2884 | const src_ptr = try self.resolveInst(extra.lhs); |
| 2725 | 2885 | const src_ptr_ty = self.air.typeOf(extra.lhs); |
| 2726 | 2886 | const len = try self.resolveInst(extra.rhs); |
| 2727 | const u8_llvm_ty = self.context.intType(8); | |
| 2728 | const ptr_u8_llvm_ty = u8_llvm_ty.pointerType(0); | |
| 2729 | const dest_ptr_u8 = self.builder.buildBitCast(dest_ptr, ptr_u8_llvm_ty, ""); | |
| 2730 | const src_ptr_u8 = self.builder.buildBitCast(src_ptr, ptr_u8_llvm_ty, ""); | |
| 2887 | const llvm_ptr_u8 = self.context.intType(8).pointerType(0); | |
| 2888 | const dest_ptr_u8 = self.builder.buildBitCast(dest_ptr, llvm_ptr_u8, ""); | |
| 2889 | const src_ptr_u8 = self.builder.buildBitCast(src_ptr, llvm_ptr_u8, ""); | |
| 2731 | 2890 | const is_volatile = src_ptr_ty.isVolatilePtr() or dest_ptr_ty.isVolatilePtr(); |
| 2732 | 2891 | const target = self.dg.module.getTarget(); |
| 2733 | 2892 | _ = self.builder.buildMemCpy( |
| ... | ... | @@ -2843,7 +3002,10 @@ pub const FuncGen = struct { |
| 2843 | 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 | 3009 | const llvm_inst = self.builder.buildLoad(ptr, ""); |
| 2848 | 3010 | const target = self.dg.module.getTarget(); |
| 2849 | 3011 | llvm_inst.setAlignment(ptr_ty.ptrAlignment(target)); |
| ... | ... | @@ -2856,12 +3018,31 @@ pub const FuncGen = struct { |
| 2856 | 3018 | ptr: *const llvm.Value, |
| 2857 | 3019 | ptr_ty: Type, |
| 2858 | 3020 | elem: *const llvm.Value, |
| 2859 | ) *const llvm.Value { | |
| 2860 | const llvm_inst = self.builder.buildStore(elem, ptr); | |
| 3021 | ordering: llvm.AtomicOrdering, | |
| 3022 | ) void { | |
| 3023 | const elem_ty = ptr_ty.childType(); | |
| 3024 | if (!elem_ty.hasCodeGenBits()) { | |
| 3025 | return; | |
| 3026 | } | |
| 2861 | 3027 | const target = self.dg.module.getTarget(); |
| 2862 | llvm_inst.setAlignment(ptr_ty.ptrAlignment(target)); | |
| 2863 | llvm_inst.setVolatile(llvm.Bool.fromBool(ptr_ty.isVolatilePtr())); | |
| 2864 | return llvm_inst; | |
| 3028 | if (!isByRef(elem_ty)) { | |
| 3029 | const store_inst = self.builder.buildStore(elem, ptr); | |
| 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 | 3294 | } |
| 3114 | 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 | 163 | |
| 164 | 164 | pub const deleteFunction = LLVMDeleteFunction; |
| 165 | 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 | 180 | pub const Type = opaque { |
| ... | ... | @@ -292,12 +304,6 @@ pub const VerifierFailureAction = enum(c_int) { |
| 292 | 304 | pub const constNeg = LLVMConstNeg; |
| 293 | 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 | 307 | pub const getEnumAttributeKindForName = LLVMGetEnumAttributeKindForName; |
| 302 | 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 | 128 | .bool_and, |
| 129 | 129 | .bool_or, |
| 130 | 130 | .store, |
| 131 | .array_elem_val, | |
| 131 | 132 | .slice_elem_val, |
| 132 | 133 | .ptr_slice_elem_val, |
| 133 | 134 | .ptr_elem_val, |
| ... | ... | @@ -150,6 +151,7 @@ const Writer = struct { |
| 150 | 151 | .ptrtoint, |
| 151 | 152 | .bool_to_int, |
| 152 | 153 | .ret, |
| 154 | .ret_load, | |
| 153 | 155 | => try w.writeUnOp(s, inst), |
| 154 | 156 | |
| 155 | 157 | .breakpoint, |
| ... | ... | @@ -158,6 +160,7 @@ const Writer = struct { |
| 158 | 160 | |
| 159 | 161 | .const_ty, |
| 160 | 162 | .alloc, |
| 163 | .ret_ptr, | |
| 161 | 164 | => try w.writeTy(s, inst), |
| 162 | 165 | |
| 163 | 166 | .not, |
src/type.zig+39-37| ... | ... | @@ -1707,32 +1707,10 @@ pub const Type = extern union { |
| 1707 | 1707 | const int_tag_ty = self.intTagType(&buffer); |
| 1708 | 1708 | return int_tag_ty.abiAlignment(target); |
| 1709 | 1709 | }, |
| 1710 | .union_tagged => { | |
| 1711 | const union_obj = self.castTag(.union_tagged).?.data; | |
| 1712 | var biggest: u32 = union_obj.tag_ty.abiAlignment(target); | |
| 1713 | for (union_obj.fields.values()) |field| { | |
| 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 | }, | |
| 1710 | // TODO pass `true` for have_tag when unions have a safety tag | |
| 1711 | .@"union" => return self.castTag(.@"union").?.data.abiAlignment(target, false), | |
| 1712 | .union_tagged => return self.castTag(.union_tagged).?.data.abiAlignment(target, true), | |
| 1713 | ||
| 1736 | 1714 | .c_void, |
| 1737 | 1715 | .void, |
| 1738 | 1716 | .type, |
| ... | ... | @@ -1790,6 +1768,7 @@ pub const Type = extern union { |
| 1790 | 1768 | const is_packed = s.layout == .Packed; |
| 1791 | 1769 | if (is_packed) @panic("TODO packed structs"); |
| 1792 | 1770 | var size: u64 = 0; |
| 1771 | var big_align: u32 = 0; | |
| 1793 | 1772 | for (s.fields.values()) |field| { |
| 1794 | 1773 | if (!field.ty.hasCodeGenBits()) continue; |
| 1795 | 1774 | |
| ... | ... | @@ -1797,12 +1776,14 @@ pub const Type = extern union { |
| 1797 | 1776 | if (field.abi_align.tag() == .abi_align_default) { |
| 1798 | 1777 | break :a field.ty.abiAlignment(target); |
| 1799 | 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 | 1783 | size = std.mem.alignForwardGeneric(u64, size, field_align); |
| 1804 | 1784 | size += field.ty.abiSize(target); |
| 1805 | 1785 | } |
| 1786 | size = std.mem.alignForwardGeneric(u64, size, big_align); | |
| 1806 | 1787 | return size; |
| 1807 | 1788 | }, |
| 1808 | 1789 | .enum_simple, .enum_full, .enum_nonexhaustive, .enum_numbered => { |
| ... | ... | @@ -1810,9 +1791,9 @@ pub const Type = extern union { |
| 1810 | 1791 | const int_tag_ty = self.intTagType(&buffer); |
| 1811 | 1792 | return int_tag_ty.abiSize(target); |
| 1812 | 1793 | }, |
| 1813 | .@"union", .union_tagged => { | |
| 1814 | @panic("TODO abiSize unions"); | |
| 1815 | }, | |
| 1794 | // TODO pass `true` for have_tag when unions have a safety tag | |
| 1795 | .@"union" => return self.castTag(.@"union").?.data.abiSize(target, false), | |
| 1796 | .union_tagged => return self.castTag(.union_tagged).?.data.abiSize(target, true), | |
| 1816 | 1797 | |
| 1817 | 1798 | .u1, |
| 1818 | 1799 | .u8, |
| ... | ... | @@ -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 | 2539 | pub fn unionFieldType(ty: Type, enum_tag: Value) Type { |
| 2554 | 2540 | const union_obj = ty.cast(Payload.Union).?.data; |
| 2555 | 2541 | const index = union_obj.tag_ty.enumTagFieldIndex(enum_tag).?; |
| ... | ... | @@ -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 | 2647 | pub fn intInfo(self: Type, target: Target) struct { signedness: std.builtin.Signedness, bits: u16 } { |
| 2662 | 2648 | var ty = self; |
| 2663 | 2649 | while (true) switch (ty.tag()) { |
| ... | ... | @@ -2700,6 +2686,11 @@ pub const Type = extern union { |
| 2700 | 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 | 2694 | else => unreachable, |
| 2704 | 2695 | }; |
| 2705 | 2696 | } |
| ... | ... | @@ -3151,12 +3142,12 @@ pub const Type = extern union { |
| 3151 | 3142 | |
| 3152 | 3143 | /// Asserts the type is an enum or a union. |
| 3153 | 3144 | /// TODO support unions |
| 3154 | pub fn intTagType(self: Type, buffer: *Payload.Bits) Type { | |
| 3155 | switch (self.tag()) { | |
| 3156 | .enum_full, .enum_nonexhaustive => return self.cast(Payload.EnumFull).?.data.tag_ty, | |
| 3157 | .enum_numbered => return self.castTag(.enum_numbered).?.data.tag_ty, | |
| 3145 | pub fn intTagType(ty: Type, buffer: *Payload.Bits) Type { | |
| 3146 | switch (ty.tag()) { | |
| 3147 | .enum_full, .enum_nonexhaustive => return ty.cast(Payload.EnumFull).?.data.tag_ty, | |
| 3148 | .enum_numbered => return ty.castTag(.enum_numbered).?.data.tag_ty, | |
| 3158 | 3149 | .enum_simple => { |
| 3159 | const enum_simple = self.castTag(.enum_simple).?.data; | |
| 3150 | const enum_simple = ty.castTag(.enum_simple).?.data; | |
| 3160 | 3151 | const bits = std.math.log2_int_ceil(usize, enum_simple.fields.count()); |
| 3161 | 3152 | buffer.* = .{ |
| 3162 | 3153 | .base = .{ .tag = .int_unsigned }, |
| ... | ... | @@ -3164,6 +3155,7 @@ pub const Type = extern union { |
| 3164 | 3155 | }; |
| 3165 | 3156 | return Type.initPayload(&buffer.base); |
| 3166 | 3157 | }, |
| 3158 | .union_tagged => return ty.castTag(.union_tagged).?.data.tag_ty.intTagType(buffer), | |
| 3167 | 3159 | else => unreachable, |
| 3168 | 3160 | } |
| 3169 | 3161 | } |
| ... | ... | @@ -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 | 3322 | pub fn structFieldCount(ty: Type) usize { |
| 3321 | 3323 | switch (ty.tag()) { |
| 3322 | 3324 | .@"struct" => { |
| ... | ... | @@ -3815,7 +3817,7 @@ pub const Type = extern union { |
| 3815 | 3817 | bit_offset: u16 = 0, |
| 3816 | 3818 | host_size: u16 = 0, |
| 3817 | 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 | 3821 | @"volatile": bool = false, |
| 3820 | 3822 | size: std.builtin.TypeInfo.Pointer.Size = .One, |
| 3821 | 3823 | }; |
test/behavior.zig+1-1| ... | ... | @@ -15,7 +15,6 @@ test { |
| 15 | 15 | _ = @import("behavior/bugs/4769_a.zig"); |
| 16 | 16 | _ = @import("behavior/bugs/4769_b.zig"); |
| 17 | 17 | _ = @import("behavior/bugs/6850.zig"); |
| 18 | _ = @import("behavior/bugs/9584.zig"); | |
| 19 | 18 | _ = @import("behavior/call.zig"); |
| 20 | 19 | _ = @import("behavior/cast.zig"); |
| 21 | 20 | _ = @import("behavior/defer.zig"); |
| ... | ... | @@ -104,6 +103,7 @@ test { |
| 104 | 103 | _ = @import("behavior/bugs/7047.zig"); |
| 105 | 104 | _ = @import("behavior/bugs/7003.zig"); |
| 106 | 105 | _ = @import("behavior/bugs/7250.zig"); |
| 106 | _ = @import("behavior/bugs/9584.zig"); | |
| 107 | 107 | _ = @import("behavior/byteswap.zig"); |
| 108 | 108 | _ = @import("behavior/byval_arg_var.zig"); |
| 109 | 109 | _ = @import("behavior/call_stage1.zig"); |
test/behavior/array.zig+26| ... | ... | @@ -50,3 +50,29 @@ test "array literal with inferred length" { |
| 50 | 50 | try expect(hex_mult.len == 4); |
| 51 | 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 | 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 | 42 | test "nested arrays" { |
| 54 | 43 | const array_of_strings = [_][]const u8{ "hello", "this", "is", "my", "thing" }; |
| 55 | 44 | for (array_of_strings) |s, i| { |
| ... | ... | @@ -76,24 +65,6 @@ test "set global var array via slice embedded in struct" { |
| 76 | 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 | 68 | test "single-item pointer to array indexing and slicing" { |
| 98 | 69 | try testSingleItemPtrArrayIndexSlice(); |
| 99 | 70 | comptime try testSingleItemPtrArrayIndexSlice(); |
test/behavior/bugs/9584.zig+1| ... | ... | @@ -57,4 +57,5 @@ test "bug 9584" { |
| 57 | 57 | .x = flags, |
| 58 | 58 | }; |
| 59 | 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 | 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 | 72 | } |
| 73 | 73 | }; |
| 74 | 74 | |
| 75 | test "return empty struct from fn" { | |
| 76 | _ = testReturnEmptyStructFromFn(); | |
| 77 | } | |
| 78 | 75 | const EmptyStruct2 = struct {}; |
| 79 | 76 | fn testReturnEmptyStructFromFn() EmptyStruct2 { |
| 80 | 77 | return EmptyStruct2{}; |