| ... | ... | @@ -175,7 +175,158 @@ fn resolveValue(self: *FuncGen, val: Value) Allocator.Error!Builder.Constant { |
| 175 | 175 | } |
| 176 | 176 | } |
| 177 | 177 | |
| 178 | | pub fn genBody(self: *FuncGen, body: []const Air.Inst.Index, coverage_point: Air.CoveragePoint) TodoError!void { |
| 178 | /// Populates `fg.ret_ptr`, `fg.err_ret_trace`, and `fg.args` based on the parameters of the |
| 179 | /// function type, then generates the entire function body. |
| 180 | /// |
| 181 | /// The caller may initialize `fg.ret_ptr`, `fg.err_ret_trace`, and `fg.args` to undefined. |
| 182 | pub fn genMainBody(fg: *FuncGen) TodoError!void { |
| 183 | const o = fg.object; |
| 184 | const zcu = o.zcu; |
| 185 | const ip = &zcu.intern_pool; |
| 186 | const comp = zcu.comp; |
| 187 | const gpa = comp.gpa; |
| 188 | |
| 189 | const fn_ty: Type = .fromInterned(ip.getNav(fg.nav_index).resolved.?.type); |
| 190 | const fn_info = zcu.typeToFunc(fn_ty).?; |
| 191 | const param_types = fn_info.param_types.get(ip); |
| 192 | |
| 193 | var it = iterateParamTypes(o, fn_info); |
| 194 | |
| 195 | // Populate `fg.ret_ptr`... |
| 196 | if (firstParamSRet(fn_info, zcu, zcu.getTarget())) { |
| 197 | fg.ret_ptr = fg.wip.arg(it.llvm_index); |
| 198 | it.llvm_index += 1; |
| 199 | } else { |
| 200 | fg.ret_ptr = .none; |
| 201 | } |
| 202 | // ...and `fg.err_ret_trace`... |
| 203 | if (fn_info.cc == .auto and comp.config.any_error_tracing) { |
| 204 | fg.err_ret_trace = fg.wip.arg(it.llvm_index); |
| 205 | it.llvm_index += 1; |
| 206 | } else { |
| 207 | fg.err_ret_trace = .none; |
| 208 | } |
| 209 | // ...and as for `fg.args`, we'll put all of the arguments into this ArrayList, and once that's |
| 210 | // done we'll use its buffer as `fg.args`. |
| 211 | var args: std.ArrayList(Builder.Value) = .empty; |
| 212 | defer args.deinit(gpa); |
| 213 | |
| 214 | while (try it.next()) |lowering| { |
| 215 | try args.ensureUnusedCapacity(gpa, 1); |
| 216 | |
| 217 | switch (lowering) { |
| 218 | .no_bits => continue, |
| 219 | .byval => { |
| 220 | assert(!it.byval_attr); |
| 221 | const param_index = it.zig_index - 1; |
| 222 | const param_ty: Type = .fromInterned(param_types[param_index]); |
| 223 | const param = fg.wip.arg(it.llvm_index - 1); |
| 224 | |
| 225 | if (isByRef(param_ty, zcu)) { |
| 226 | const alignment = param_ty.abiAlignment(zcu).toLlvm(); |
| 227 | const arg_ptr = try fg.buildAlloca(try o.lowerType(param_ty), alignment); |
| 228 | // We don't need to handle non-ABI-sized integer types in memory here since they |
| 229 | // are never by-ref. |
| 230 | _ = try fg.wip.store(.normal, param, arg_ptr, alignment); |
| 231 | args.appendAssumeCapacity(arg_ptr); |
| 232 | } else { |
| 233 | args.appendAssumeCapacity(param); |
| 234 | } |
| 235 | }, |
| 236 | .byref, .byref_mut => { |
| 237 | const param_ty: Type = .fromInterned(param_types[it.zig_index - 1]); |
| 238 | const param = fg.wip.arg(it.llvm_index - 1); |
| 239 | |
| 240 | if (isByRef(param_ty, zcu)) { |
| 241 | args.appendAssumeCapacity(param); |
| 242 | } else { |
| 243 | args.appendAssumeCapacity(try fg.load(param, .none, param_ty, .normal)); |
| 244 | } |
| 245 | }, |
| 246 | .abi_sized_int => { |
| 247 | assert(!it.byval_attr); |
| 248 | const param_ty: Type = .fromInterned(param_types[it.zig_index - 1]); |
| 249 | const param = fg.wip.arg(it.llvm_index - 1); |
| 250 | |
| 251 | const param_llvm_ty = try o.lowerType(param_ty); |
| 252 | const alignment = param_ty.abiAlignment(zcu).toLlvm(); |
| 253 | const arg_ptr = try fg.buildAlloca(param_llvm_ty, alignment); |
| 254 | _ = try fg.wip.store(.normal, param, arg_ptr, alignment); |
| 255 | |
| 256 | if (isByRef(param_ty, zcu)) { |
| 257 | args.appendAssumeCapacity(arg_ptr); |
| 258 | } else { |
| 259 | args.appendAssumeCapacity(try fg.load(arg_ptr, .none, param_ty, .normal)); |
| 260 | } |
| 261 | }, |
| 262 | .slice => { |
| 263 | assert(!it.byval_attr); |
| 264 | const param_ty: Type = .fromInterned(param_types[it.zig_index - 1]); |
| 265 | assert(!isByRef(param_ty, zcu)); |
| 266 | const slice_val = try fg.wip.buildAggregate( |
| 267 | try o.lowerType(param_ty), |
| 268 | &.{ fg.wip.arg(it.llvm_index - 2), fg.wip.arg(it.llvm_index - 1) }, |
| 269 | "", |
| 270 | ); |
| 271 | args.appendAssumeCapacity(slice_val); |
| 272 | }, |
| 273 | .multiple_llvm_types => { |
| 274 | assert(!it.byval_attr); |
| 275 | const param_ty: Type = .fromInterned(param_types[it.zig_index - 1]); |
| 276 | const param_alignment = param_ty.abiAlignment(zcu); |
| 277 | const llvm_ty = try o.builder.arrayType(it.offsets_buffer[it.types_len], .i8); |
| 278 | const arg_ptr = try fg.buildAlloca(llvm_ty, param_alignment.toLlvm()); |
| 279 | const llvm_args_start = it.llvm_index - it.types_len; |
| 280 | for (llvm_args_start.., it.offsets_buffer[0..it.types_len]) |llvm_arg_index, offset| { |
| 281 | const param = fg.wip.arg(@intCast(llvm_arg_index)); |
| 282 | const part_ptr = try fg.ptraddConst(arg_ptr, offset); |
| 283 | _ = try fg.wip.store(.normal, param, part_ptr, param_alignment.offset(offset).toLlvm()); |
| 284 | } |
| 285 | |
| 286 | if (isByRef(param_ty, zcu)) { |
| 287 | args.appendAssumeCapacity(arg_ptr); |
| 288 | } else { |
| 289 | args.appendAssumeCapacity(try fg.load(arg_ptr, .none, param_ty, .normal)); |
| 290 | } |
| 291 | }, |
| 292 | .float_array => { |
| 293 | const param_ty: Type = .fromInterned(param_types[it.zig_index - 1]); |
| 294 | const param_llvm_ty = try o.lowerType(param_ty); |
| 295 | const param = fg.wip.arg(it.llvm_index - 1); |
| 296 | |
| 297 | const alignment = param_ty.abiAlignment(zcu).toLlvm(); |
| 298 | const arg_ptr = try fg.buildAlloca(param_llvm_ty, alignment); |
| 299 | _ = try fg.wip.store(.normal, param, arg_ptr, alignment); |
| 300 | |
| 301 | if (isByRef(param_ty, zcu)) { |
| 302 | args.appendAssumeCapacity(arg_ptr); |
| 303 | } else { |
| 304 | args.appendAssumeCapacity(try fg.load(arg_ptr, .none, param_ty, .normal)); |
| 305 | } |
| 306 | }, |
| 307 | .i32_array, .i64_array => { |
| 308 | const param_ty: Type = .fromInterned(param_types[it.zig_index - 1]); |
| 309 | const param = fg.wip.arg(it.llvm_index - 1); |
| 310 | |
| 311 | const alignment = param_ty.abiAlignment(zcu).toLlvm(); |
| 312 | const arg_ptr = try fg.buildAlloca(param.typeOfWip(&fg.wip), alignment); |
| 313 | _ = try fg.wip.store(.normal, param, arg_ptr, alignment); |
| 314 | |
| 315 | if (isByRef(param_ty, zcu)) { |
| 316 | args.appendAssumeCapacity(arg_ptr); |
| 317 | } else { |
| 318 | args.appendAssumeCapacity(try fg.load(arg_ptr, .none, param_ty, .normal)); |
| 319 | } |
| 320 | }, |
| 321 | } |
| 322 | } |
| 323 | |
| 324 | fg.args = args.items; |
| 325 | |
| 326 | try fg.genBody(fg.air.getMainBody(), .poi); |
| 327 | } |
| 328 | |
| 329 | fn genBody(self: *FuncGen, body: []const Air.Inst.Index, coverage_point: Air.CoveragePoint) TodoError!void { |
| 179 | 330 | const o = self.object; |
| 180 | 331 | const zcu = self.object.zcu; |
| 181 | 332 | const ip = &zcu.intern_pool; |
| ... | ... | @@ -400,8 +551,8 @@ pub fn genBody(self: *FuncGen, body: []const Air.Inst.Index, coverage_point: Air |
| 400 | 551 | .optional_payload_ptr => try self.airOptionalPayloadPtr(inst), |
| 401 | 552 | .optional_payload_ptr_set => try self.airOptionalPayloadPtrSet(inst), |
| 402 | 553 | |
| 403 | | .unwrap_errunion_payload => try self.airErrUnionPayload(inst, false), |
| 404 | | .unwrap_errunion_payload_ptr => try self.airErrUnionPayload(inst, true), |
| 554 | .unwrap_errunion_payload => try self.airErrUnionPayload(inst), |
| 555 | .unwrap_errunion_payload_ptr => try self.airErrUnionPayloadPtr(inst), |
| 405 | 556 | .unwrap_errunion_err => try self.airErrUnionErr(inst, false), |
| 406 | 557 | .unwrap_errunion_err_ptr => try self.airErrUnionErr(inst, true), |
| 407 | 558 | .errunion_payload_ptr_set => try self.airErrUnionPayloadPtrSet(inst), |
| ... | ... | @@ -644,6 +795,8 @@ fn airCall(self: *FuncGen, inst: Air.Inst.Index, modifier: std.lang.CallModifier |
| 644 | 795 | const llvm_param_ty = try o.lowerType(param_ty); |
| 645 | 796 | if (isByRef(param_ty, zcu)) { |
| 646 | 797 | const alignment = param_ty.abiAlignment(zcu).toLlvm(); |
| 798 | // We don't need to handle non-ABI-sized integer types in memory here since they are |
| 799 | // never by-ref. |
| 647 | 800 | const loaded = try self.wip.load(.normal, llvm_param_ty, llvm_arg, alignment, ""); |
| 648 | 801 | try llvm_args.append(loaded); |
| 649 | 802 | } else { |
| ... | ... | @@ -660,7 +813,7 @@ fn airCall(self: *FuncGen, inst: Air.Inst.Index, modifier: std.lang.CallModifier |
| 660 | 813 | const alignment = param_ty.abiAlignment(zcu).toLlvm(); |
| 661 | 814 | const param_llvm_ty = llvm_arg.typeOfWip(&self.wip); |
| 662 | 815 | const arg_ptr = try self.buildAlloca(param_llvm_ty, alignment); |
| 663 | | _ = try self.wip.store(.normal, llvm_arg, arg_ptr, alignment); |
| 816 | try self.store(arg_ptr, .none, llvm_arg, param_ty, .normal); |
| 664 | 817 | try llvm_args.append(arg_ptr); |
| 665 | 818 | } |
| 666 | 819 | }, |
| ... | ... | @@ -672,12 +825,7 @@ fn airCall(self: *FuncGen, inst: Air.Inst.Index, modifier: std.lang.CallModifier |
| 672 | 825 | const alignment = param_ty.abiAlignment(zcu).toLlvm(); |
| 673 | 826 | const param_llvm_ty = try o.lowerType(param_ty); |
| 674 | 827 | const arg_ptr = try self.buildAlloca(param_llvm_ty, alignment); |
| 675 | | if (isByRef(param_ty, zcu)) { |
| 676 | | const loaded = try self.wip.load(.normal, param_llvm_ty, llvm_arg, alignment, ""); |
| 677 | | _ = try self.wip.store(.normal, loaded, arg_ptr, alignment); |
| 678 | | } else { |
| 679 | | _ = try self.wip.store(.normal, llvm_arg, arg_ptr, alignment); |
| 680 | | } |
| 828 | try self.store(arg_ptr, .none, llvm_arg, param_ty, .normal); |
| 681 | 829 | try llvm_args.append(arg_ptr); |
| 682 | 830 | }, |
| 683 | 831 | .abi_sized_int => { |
| ... | ... | @@ -694,9 +842,9 @@ fn airCall(self: *FuncGen, inst: Air.Inst.Index, modifier: std.lang.CallModifier |
| 694 | 842 | // LLVM does not allow bitcasting structs so we must allocate |
| 695 | 843 | // a local, store as one type, and then load as another type. |
| 696 | 844 | const alignment = param_ty.abiAlignment(zcu).toLlvm(); |
| 697 | | const int_ptr = try self.buildAlloca(int_llvm_ty, alignment); |
| 698 | | _ = try self.wip.store(.normal, llvm_arg, int_ptr, alignment); |
| 699 | | const loaded = try self.wip.load(.normal, int_llvm_ty, int_ptr, alignment, ""); |
| 845 | const ptr = try self.buildAlloca(int_llvm_ty, alignment); |
| 846 | try self.store(ptr, .none, llvm_arg, param_ty, .normal); |
| 847 | const loaded = try self.wip.load(.normal, int_llvm_ty, ptr, alignment, ""); |
| 700 | 848 | try llvm_args.append(loaded); |
| 701 | 849 | } |
| 702 | 850 | }, |
| ... | ... | @@ -711,19 +859,10 @@ fn airCall(self: *FuncGen, inst: Air.Inst.Index, modifier: std.lang.CallModifier |
| 711 | 859 | const arg = args[it.zig_index - 1]; |
| 712 | 860 | const param_ty = self.typeOf(arg); |
| 713 | 861 | const llvm_arg = try self.resolveInst(arg); |
| 714 | | const is_by_ref = isByRef(param_ty, zcu); |
| 715 | 862 | const param_alignment = param_ty.abiAlignment(zcu); |
| 716 | 863 | const llvm_ty = try o.builder.arrayType(it.offsets_buffer[it.types_len], .i8); |
| 717 | 864 | const arg_ptr = try self.buildAlloca(llvm_ty, param_alignment.toLlvm()); |
| 718 | | if (is_by_ref) _ = try self.wip.callMemCpy( |
| 719 | | arg_ptr, |
| 720 | | param_alignment.toLlvm(), |
| 721 | | llvm_arg, |
| 722 | | param_alignment.toLlvm(), |
| 723 | | try o.builder.intValue(try o.lowerType(.usize), param_ty.abiSize(zcu)), |
| 724 | | .normal, |
| 725 | | self.disable_intrinsics, |
| 726 | | ) else _ = try self.wip.store(.normal, llvm_arg, arg_ptr, param_alignment.toLlvm()); |
| 865 | try self.store(arg_ptr, .none, llvm_arg, param_ty, .normal); |
| 727 | 866 | |
| 728 | 867 | try llvm_args.ensureUnusedCapacity(it.types_len); |
| 729 | 868 | for (it.types_buffer[0..it.types_len], it.offsets_buffer[0..it.types_len]) |field_ty, offset| { |
| ... | ... | @@ -735,35 +874,38 @@ fn airCall(self: *FuncGen, inst: Air.Inst.Index, modifier: std.lang.CallModifier |
| 735 | 874 | .float_array => |count| { |
| 736 | 875 | const arg = args[it.zig_index - 1]; |
| 737 | 876 | const arg_ty = self.typeOf(arg); |
| 738 | | var llvm_arg = try self.resolveInst(arg); |
| 739 | | const alignment = arg_ty.abiAlignment(zcu).toLlvm(); |
| 740 | | if (!isByRef(arg_ty, zcu)) { |
| 741 | | const ptr = try self.buildAlloca(llvm_arg.typeOfWip(&self.wip), alignment); |
| 742 | | _ = try self.wip.store(.normal, llvm_arg, ptr, alignment); |
| 743 | | llvm_arg = ptr; |
| 744 | | } |
| 877 | const arg_val = try self.resolveInst(arg); |
| 878 | |
| 879 | const arg_align = arg_ty.abiAlignment(zcu); |
| 880 | |
| 881 | const arg_ptr: Builder.Value = if (!isByRef(arg_ty, zcu)) ptr: { |
| 882 | const ptr = try self.buildAlloca(try o.lowerType(arg_ty), arg_align.toLlvm()); |
| 883 | try self.store(ptr, .none, arg_val, arg_ty, .normal); |
| 884 | break :ptr ptr; |
| 885 | } else arg_val; |
| 745 | 886 | |
| 746 | 887 | const float_ty = try o.lowerType(aarch64_c_abi.getFloatArrayType(arg_ty, zcu).?); |
| 747 | 888 | const array_ty = try o.builder.arrayType(count, float_ty); |
| 748 | 889 | |
| 749 | | const loaded = try self.wip.load(.normal, array_ty, llvm_arg, alignment, ""); |
| 890 | const loaded = try self.wip.load(.normal, array_ty, arg_ptr, arg_align.toLlvm(), ""); |
| 750 | 891 | try llvm_args.append(loaded); |
| 751 | 892 | }, |
| 752 | 893 | .i32_array, .i64_array => |arr_len| { |
| 753 | 894 | const elem_size: u8 = if (lowering == .i32_array) 32 else 64; |
| 754 | 895 | const arg = args[it.zig_index - 1]; |
| 755 | 896 | const arg_ty = self.typeOf(arg); |
| 756 | | var llvm_arg = try self.resolveInst(arg); |
| 757 | | const alignment = arg_ty.abiAlignment(zcu).toLlvm(); |
| 758 | | if (!isByRef(arg_ty, zcu)) { |
| 759 | | const ptr = try self.buildAlloca(llvm_arg.typeOfWip(&self.wip), alignment); |
| 760 | | _ = try self.wip.store(.normal, llvm_arg, ptr, alignment); |
| 761 | | llvm_arg = ptr; |
| 762 | | } |
| 897 | const arg_val = try self.resolveInst(arg); |
| 763 | 898 | |
| 764 | | const array_ty = |
| 765 | | try o.builder.arrayType(arr_len, try o.builder.intType(@intCast(elem_size))); |
| 766 | | const loaded = try self.wip.load(.normal, array_ty, llvm_arg, alignment, ""); |
| 899 | const arg_align = arg_ty.abiAlignment(zcu); |
| 900 | |
| 901 | const arg_ptr: Builder.Value = if (!isByRef(arg_ty, zcu)) ptr: { |
| 902 | const ptr = try self.buildAlloca(try o.lowerType(arg_ty), arg_align.toLlvm()); |
| 903 | try self.store(ptr, .none, arg_val, arg_ty, .normal); |
| 904 | break :ptr ptr; |
| 905 | } else arg_val; |
| 906 | |
| 907 | const array_ty = try o.builder.arrayType(arr_len, try o.builder.intType(@intCast(elem_size))); |
| 908 | const loaded = try self.wip.load(.normal, array_ty, arg_ptr, arg_align.toLlvm(), ""); |
| 767 | 909 | try llvm_args.append(loaded); |
| 768 | 910 | }, |
| 769 | 911 | }; |
| ... | ... | @@ -875,8 +1017,7 @@ fn airCall(self: *FuncGen, inst: Air.Inst.Index, modifier: std.lang.CallModifier |
| 875 | 1017 | return rp; |
| 876 | 1018 | } else { |
| 877 | 1019 | // our by-ref status disagrees with sret so we must load. |
| 878 | | const return_alignment = return_type.abiAlignment(zcu).toLlvm(); |
| 879 | | return self.wip.load(.normal, llvm_ret_ty, rp, return_alignment, ""); |
| 1020 | return self.load(rp, .none, return_type, .normal); |
| 880 | 1021 | } |
| 881 | 1022 | } |
| 882 | 1023 | |
| ... | ... | @@ -888,11 +1029,14 @@ fn airCall(self: *FuncGen, inst: Air.Inst.Index, modifier: std.lang.CallModifier |
| 888 | 1029 | // by using our canonical type, then loading it if necessary. |
| 889 | 1030 | const alignment = return_type.abiAlignment(zcu).toLlvm(); |
| 890 | 1031 | const rp = try self.buildAlloca(abi_ret_ty, alignment); |
| 1032 | // We don't need to handle non-ABI-sized integer types in memory here since they can only be |
| 1033 | // returned from `CallingConvention.auto` functions, in which case `abi_ret_ty` will equal |
| 1034 | // `llvm_ret_ty` anyway. |
| 891 | 1035 | _ = try self.wip.store(.normal, call, rp, alignment); |
| 892 | 1036 | return if (isByRef(return_type, zcu)) |
| 893 | 1037 | rp |
| 894 | 1038 | else |
| 895 | | try self.wip.load(.normal, llvm_ret_ty, rp, alignment, ""); |
| 1039 | try self.load(rp, .none, return_type, .normal); |
| 896 | 1040 | } |
| 897 | 1041 | |
| 898 | 1042 | if (isByRef(return_type, zcu)) { |
| ... | ... | @@ -900,6 +1044,7 @@ fn airCall(self: *FuncGen, inst: Air.Inst.Index, modifier: std.lang.CallModifier |
| 900 | 1044 | // and return the allocation pointer. |
| 901 | 1045 | const alignment = return_type.abiAlignment(zcu).toLlvm(); |
| 902 | 1046 | const rp = try self.buildAlloca(llvm_ret_ty, alignment); |
| 1047 | // We don't need to handle non-ABI-sized integer types here since they are never by-ref. |
| 903 | 1048 | _ = try self.wip.store(.normal, call, rp, alignment); |
| 904 | 1049 | return rp; |
| 905 | 1050 | } else { |
| ... | ... | @@ -969,12 +1114,7 @@ fn airRet(self: *FuncGen, inst: Air.Inst.Index, safety: bool) Allocator.Error!vo |
| 969 | 1114 | return; |
| 970 | 1115 | } |
| 971 | 1116 | |
| 972 | | try self.store( |
| 973 | | self.ret_ptr, |
| 974 | | .none, |
| 975 | | operand, |
| 976 | | ret_ty, |
| 977 | | ); |
| 1117 | try self.store(self.ret_ptr, .none, operand, ret_ty, .normal); |
| 978 | 1118 | _ = try self.wip.retVoid(); |
| 979 | 1119 | return; |
| 980 | 1120 | } |
| ... | ... | @@ -991,18 +1131,18 @@ fn airRet(self: *FuncGen, inst: Air.Inst.Index, safety: bool) Allocator.Error!vo |
| 991 | 1131 | return; |
| 992 | 1132 | } |
| 993 | 1133 | |
| 1134 | const llvm_ret_ty = try o.lowerType(ret_ty); |
| 994 | 1135 | const abi_ret_ty = try lowerFnRetTy(o, fn_info); |
| 995 | 1136 | const operand = try self.resolveInst(un_op); |
| 996 | 1137 | const val_is_undef = if (un_op.toInterned()) |i| Value.fromInterned(i).isUndef(zcu) else false; |
| 997 | | const alignment = ret_ty.abiAlignment(zcu).toLlvm(); |
| 1138 | const ret_ty_align = ret_ty.abiAlignment(zcu); |
| 998 | 1139 | |
| 999 | 1140 | if (val_is_undef and safety and !self.needMemsetWorkaround(ret_ty.abiSize(zcu))) { |
| 1000 | | const llvm_ret_ty = operand.typeOfWip(&self.wip); |
| 1001 | | const rp = try self.buildAlloca(llvm_ret_ty, alignment); |
| 1141 | const rp = try self.buildAlloca(llvm_ret_ty, ret_ty_align.toLlvm()); |
| 1002 | 1142 | const len = try o.builder.intValue(try o.lowerType(.usize), ret_ty.abiSize(zcu)); |
| 1003 | 1143 | _ = try self.wip.callMemSet( |
| 1004 | 1144 | rp, |
| 1005 | | alignment, |
| 1145 | ret_ty_align.toLlvm(), |
| 1006 | 1146 | try o.builder.intValue(.i8, 0xaa), |
| 1007 | 1147 | len, |
| 1008 | 1148 | .normal, |
| ... | ... | @@ -1012,27 +1152,37 @@ fn airRet(self: *FuncGen, inst: Air.Inst.Index, safety: bool) Allocator.Error!vo |
| 1012 | 1152 | if (owner_mod.valgrind) { |
| 1013 | 1153 | try self.valgrindMarkUndef(rp, len); |
| 1014 | 1154 | } |
| 1015 | | _ = try self.wip.ret(try self.wip.load(.normal, abi_ret_ty, rp, alignment, "")); |
| 1155 | if (fn_info.cc == .auto and abi_ret_ty == llvm_ret_ty) { |
| 1156 | assert(!isByRef(ret_ty, zcu)); |
| 1157 | // The return type could be a non-ABI-sized integer, so use `FuncGen.load` to make sure |
| 1158 | // we load it from memory correctly. |
| 1159 | const loaded = try self.load(rp, .none, ret_ty, .normal); |
| 1160 | _ = try self.wip.ret(loaded); |
| 1161 | } else { |
| 1162 | const loaded = try self.wip.load(.normal, abi_ret_ty, rp, ret_ty_align.toLlvm(), ""); |
| 1163 | _ = try self.wip.ret(loaded); |
| 1164 | } |
| 1016 | 1165 | return; |
| 1017 | 1166 | } |
| 1018 | 1167 | |
| 1019 | 1168 | if (isByRef(ret_ty, zcu)) { |
| 1020 | | // operand is a pointer however self.ret_ptr is null so that means |
| 1021 | | // we need to return a value. |
| 1022 | | _ = try self.wip.ret(try self.wip.load(.normal, abi_ret_ty, operand, alignment, "")); |
| 1169 | // operand is a pointer however self.ret_ptr is null so that means we need to return a value. |
| 1170 | // No need to handle non-ABI-sized integer types in memory here since they are never by-ref. |
| 1171 | _ = try self.wip.ret(try self.wip.load(.normal, abi_ret_ty, operand, ret_ty_align.toLlvm(), "")); |
| 1023 | 1172 | return; |
| 1024 | 1173 | } |
| 1025 | 1174 | |
| 1026 | | const llvm_ret_ty = operand.typeOfWip(&self.wip); |
| 1027 | 1175 | if (abi_ret_ty == llvm_ret_ty) { |
| 1028 | 1176 | _ = try self.wip.ret(operand); |
| 1029 | | return; |
| 1177 | } else { |
| 1178 | const rp = try self.buildAlloca(llvm_ret_ty, ret_ty_align.toLlvm()); |
| 1179 | try self.store(rp, .none, operand, ret_ty, .normal); |
| 1180 | // No need to handle non-ABI-sized integer types in memory here since they can only be |
| 1181 | // returned from `CallingConvention.auto` functions, in which case `abi_ret_ty` will equal |
| 1182 | // `llvm_ret_ty` anyway. |
| 1183 | const ret_val = try self.wip.load(.normal, abi_ret_ty, rp, ret_ty_align.toLlvm(), ""); |
| 1184 | _ = try self.wip.ret(ret_val); |
| 1030 | 1185 | } |
| 1031 | | |
| 1032 | | const rp = try self.buildAlloca(llvm_ret_ty, alignment); |
| 1033 | | _ = try self.wip.store(.normal, operand, rp, alignment); |
| 1034 | | _ = try self.wip.ret(try self.wip.load(.normal, abi_ret_ty, rp, alignment, "")); |
| 1035 | | return; |
| 1036 | 1186 | } |
| 1037 | 1187 | |
| 1038 | 1188 | fn airRetLoad(self: *FuncGen, inst: Air.Inst.Index) Allocator.Error!void { |
| ... | ... | @@ -1043,26 +1193,24 @@ fn airRetLoad(self: *FuncGen, inst: Air.Inst.Index) Allocator.Error!void { |
| 1043 | 1193 | const ptr_ty = self.typeOf(un_op); |
| 1044 | 1194 | const ret_ty = ptr_ty.childType(zcu); |
| 1045 | 1195 | const fn_info = zcu.typeToFunc(.fromInterned(ip.getNav(self.nav_index).resolved.?.type)).?; |
| 1046 | | if (!ret_ty.hasRuntimeBits(zcu)) { |
| 1047 | | if (Type.fromInterned(fn_info.return_type).isError(zcu)) { |
| 1048 | | // Functions with an empty error set are emitted with an error code |
| 1049 | | // return type and return zero so they can be function pointers coerced |
| 1050 | | // to functions that return anyerror. |
| 1051 | | _ = try self.wip.ret(try o.builder.intValue(try o.errorIntType(), 0)); |
| 1052 | | } else { |
| 1053 | | _ = try self.wip.retVoid(); |
| 1054 | | } |
| 1055 | | return; |
| 1056 | | } |
| 1057 | | if (self.ret_ptr != .none) { |
| 1196 | if (!ret_ty.hasRuntimeBits(zcu) or self.ret_ptr != .none) { |
| 1058 | 1197 | _ = try self.wip.retVoid(); |
| 1059 | 1198 | return; |
| 1060 | 1199 | } |
| 1061 | 1200 | const ptr = try self.resolveInst(un_op); |
| 1201 | const llvm_ret_ty = try o.lowerType(ret_ty); |
| 1062 | 1202 | const abi_ret_ty = try lowerFnRetTy(o, fn_info); |
| 1063 | | const alignment = ret_ty.abiAlignment(zcu).toLlvm(); |
| 1064 | | _ = try self.wip.ret(try self.wip.load(.normal, abi_ret_ty, ptr, alignment, "")); |
| 1065 | | return; |
| 1203 | if (fn_info.cc == .auto and abi_ret_ty == llvm_ret_ty) { |
| 1204 | assert(!isByRef(ret_ty, zcu)); |
| 1205 | // The return type could be a non-ABI-sized integer, so use `FuncGen.load` to make sure we |
| 1206 | // load it from memory correctly. |
| 1207 | const loaded = try self.load(ptr, .none, ret_ty, .normal); |
| 1208 | _ = try self.wip.ret(loaded); |
| 1209 | } else { |
| 1210 | const ret_ty_align = ret_ty.abiAlignment(zcu); |
| 1211 | const loaded = try self.wip.load(.normal, abi_ret_ty, ptr, ret_ty_align.toLlvm(), ""); |
| 1212 | _ = try self.wip.ret(loaded); |
| 1213 | } |
| 1066 | 1214 | } |
| 1067 | 1215 | |
| 1068 | 1216 | fn airCVaArg(self: *FuncGen, inst: Air.Inst.Index) Allocator.Error!Builder.Value { |
| ... | ... | @@ -1089,7 +1237,7 @@ fn airCVaCopy(self: *FuncGen, inst: Air.Inst.Index) Allocator.Error!Builder.Valu |
| 1089 | 1237 | return if (isByRef(va_list_ty, zcu)) |
| 1090 | 1238 | dest_list |
| 1091 | 1239 | else |
| 1092 | | try self.wip.load(.normal, llvm_va_list_ty, dest_list, result_alignment, ""); |
| 1240 | try self.load(dest_list, .none, va_list_ty, .normal); |
| 1093 | 1241 | } |
| 1094 | 1242 | |
| 1095 | 1243 | fn airCVaEnd(self: *FuncGen, inst: Air.Inst.Index) Allocator.Error!Builder.Value { |
| ... | ... | @@ -1113,7 +1261,7 @@ fn airCVaStart(self: *FuncGen, inst: Air.Inst.Index) Allocator.Error!Builder.Val |
| 1113 | 1261 | return if (isByRef(va_list_ty, zcu)) |
| 1114 | 1262 | dest_list |
| 1115 | 1263 | else |
| 1116 | | try self.wip.load(.normal, llvm_va_list_ty, dest_list, result_alignment, ""); |
| 1264 | try self.load(dest_list, .none, va_list_ty, .normal); |
| 1117 | 1265 | } |
| 1118 | 1266 | |
| 1119 | 1267 | fn airCmp( |
| ... | ... | @@ -1147,13 +1295,7 @@ fn airCmpLteErrorsLen(self: *FuncGen, inst: Air.Inst.Index) Allocator.Error!Buil |
| 1147 | 1295 | const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; |
| 1148 | 1296 | const operand = try self.resolveInst(un_op); |
| 1149 | 1297 | const errors_len_ptr = try o.getErrorsLen(); |
| 1150 | | const errors_len_val = try self.wip.load( |
| 1151 | | .normal, |
| 1152 | | try o.errorIntType(), |
| 1153 | | errors_len_ptr.toValue(&o.builder), |
| 1154 | | Type.errorAbiAlignment(o.zcu).toLlvm(), |
| 1155 | | "", |
| 1156 | | ); |
| 1298 | const errors_len_val = try self.load(errors_len_ptr.toValue(&o.builder), .none, .anyerror, .normal); |
| 1157 | 1299 | return self.wip.icmp(.ule, operand, errors_len_val, ""); |
| 1158 | 1300 | } |
| 1159 | 1301 | |
| ... | ... | @@ -1630,32 +1772,23 @@ fn lowerTry( |
| 1630 | 1772 | const payload_has_bits = payload_ty.hasRuntimeBits(zcu); |
| 1631 | 1773 | const error_type = try o.errorIntType(); |
| 1632 | 1774 | |
| 1633 | | const err_set_align: InternPool.Alignment, const payload_align: InternPool.Alignment = if (operand_is_ptr) .{ |
| 1634 | | operand_ptr_align.minStrict(Type.anyerror.abiAlignment(zcu)), |
| 1635 | | operand_ptr_align.minStrict(payload_ty.abiAlignment(zcu)), |
| 1636 | | } else .{ .none, .none }; |
| 1775 | const operand_align: InternPool.Alignment = if (operand_is_ptr) operand_ptr_align else err_union_ty.abiAlignment(zcu); |
| 1637 | 1776 | |
| 1638 | 1777 | if (!err_union_ty.errorUnionSet(zcu).errorSetIsEmpty(zcu)) { |
| 1639 | 1778 | const loaded = loaded: { |
| 1640 | | const access_kind: Builder.MemoryAccessKind = |
| 1641 | | if (err_union_ty.isVolatilePtr(zcu)) .@"volatile" else .normal; |
| 1642 | | |
| 1643 | | if (!payload_has_bits) { |
| 1644 | | break :loaded if (operand_is_ptr) |
| 1645 | | try fg.wip.load(access_kind, error_type, err_union, err_set_align.toLlvm(), "") |
| 1646 | | else |
| 1647 | | err_union; |
| 1779 | if (payload_has_bits) { |
| 1780 | assert(isByRef(err_union_ty, zcu)); // error unions are by-ref unless the payload has no bits |
| 1781 | } else if (!operand_is_ptr) { |
| 1782 | break :loaded err_union; |
| 1648 | 1783 | } |
| 1649 | 1784 | |
| 1650 | | assert(isByRef(err_union_ty, zcu)); // error unions are by-ref unless the payload has no bits |
| 1651 | 1785 | const offset = codegen.errUnionErrorOffset(payload_ty, zcu); |
| 1652 | 1786 | const err_field_ptr = try fg.ptraddConst(err_union, offset); |
| 1653 | | break :loaded try fg.wip.load( |
| 1654 | | if (operand_is_ptr) access_kind else .normal, |
| 1655 | | error_type, |
| 1787 | break :loaded try fg.load( |
| 1656 | 1788 | err_field_ptr, |
| 1657 | | err_set_align.toLlvm(), |
| 1658 | | "", |
| 1789 | operand_align.offset(offset), |
| 1790 | .anyerror, |
| 1791 | if (err_union_ty.isVolatilePtr(zcu)) .@"volatile" else .normal, |
| 1659 | 1792 | ); |
| 1660 | 1793 | }; |
| 1661 | 1794 | const zero = try o.builder.intValue(error_type, 0); |
| ... | ... | @@ -1672,15 +1805,18 @@ fn lowerTry( |
| 1672 | 1805 | fg.wip.cursor = .{ .block = continue_block }; |
| 1673 | 1806 | } |
| 1674 | 1807 | if (is_unused) return .none; |
| 1675 | | if (!payload_has_bits) return if (operand_is_ptr) err_union else .none; |
| 1676 | | assert(isByRef(err_union_ty, zcu)); // error unions are by-ref unless the payload has no bits |
| 1677 | | const payload_ptr = try fg.ptraddConst(err_union, codegen.errUnionPayloadOffset(payload_ty, zcu)); |
| 1808 | |
| 1809 | if (!operand_is_ptr) { |
| 1810 | assert(payload_has_bits); // otherwise the result should be comptime-known |
| 1811 | assert(isByRef(err_union_ty, zcu)); // error unions are by-ref unless the payload has no bits |
| 1812 | } |
| 1813 | |
| 1814 | const offset = codegen.errUnionPayloadOffset(payload_ty, zcu); |
| 1815 | const payload_ptr = try fg.ptraddConst(err_union, offset); |
| 1678 | 1816 | if (operand_is_ptr) { |
| 1679 | 1817 | return payload_ptr; |
| 1680 | | } else if (isByRef(payload_ty, zcu)) { |
| 1681 | | return fg.loadByRef(payload_ptr, payload_ty, payload_align.toLlvm(), .normal); |
| 1682 | 1818 | } else { |
| 1683 | | return fg.wip.load(.normal, try o.lowerType(payload_ty), payload_ptr, payload_align.toLlvm(), ""); |
| 1819 | return fg.load(payload_ptr, operand_align.offset(offset), payload_ty, .normal); |
| 1684 | 1820 | } |
| 1685 | 1821 | } |
| 1686 | 1822 | |
| ... | ... | @@ -2144,11 +2280,7 @@ fn airSliceElemVal(self: *FuncGen, inst: Air.Inst.Index) Allocator.Error!Builder |
| 2144 | 2280 | const elem_align = slice_ty.ptrAlignment(zcu).min(elem_ty.abiAlignment(zcu)); |
| 2145 | 2281 | const access_kind: Builder.MemoryAccessKind = if (slice_info.flags.is_volatile) .@"volatile" else .normal; |
| 2146 | 2282 | self.maybeMarkAllowZeroAccess(slice_info); |
| 2147 | | if (isByRef(elem_ty, zcu)) { |
| 2148 | | return self.loadByRef(ptr, elem_ty, elem_align.toLlvm(), access_kind); |
| 2149 | | } else { |
| 2150 | | return self.loadTruncate(access_kind, elem_ty, ptr, elem_align.toLlvm()); |
| 2151 | | } |
| 2283 | return self.load(ptr, elem_align, elem_ty, access_kind); |
| 2152 | 2284 | } |
| 2153 | 2285 | |
| 2154 | 2286 | fn airSliceElemPtr(self: *FuncGen, inst: Air.Inst.Index) Allocator.Error!Builder.Value { |
| ... | ... | @@ -2173,12 +2305,7 @@ fn airArrayElemVal(self: *FuncGen, inst: Air.Inst.Index) Allocator.Error!Builder |
| 2173 | 2305 | const elem_ty = array_ty.childType(zcu); |
| 2174 | 2306 | if (isByRef(array_ty, zcu)) { |
| 2175 | 2307 | const elem_ptr = try self.ptraddScaled(array_llvm_val, rhs, elem_ty.abiSize(zcu)); |
| 2176 | | if (isByRef(elem_ty, zcu)) { |
| 2177 | | const elem_align = elem_ty.abiAlignment(zcu).toLlvm(); |
| 2178 | | return self.loadByRef(elem_ptr, elem_ty, elem_align, .normal); |
| 2179 | | } else { |
| 2180 | | return self.loadTruncate(.normal, elem_ty, elem_ptr, .default); |
| 2181 | | } |
| 2308 | return self.load(elem_ptr, .none, elem_ty, .normal); |
| 2182 | 2309 | } |
| 2183 | 2310 | |
| 2184 | 2311 | // This branch can be reached for vectors, which are always by-value. |
| ... | ... | @@ -2197,8 +2324,8 @@ fn airPtrElemVal(self: *FuncGen, inst: Air.Inst.Index) Allocator.Error!Builder.V |
| 2197 | 2324 | |
| 2198 | 2325 | return self.load( |
| 2199 | 2326 | try self.ptraddScaled(base_ptr, rhs, elem_ty.abiSize(zcu)), |
| 2327 | ptr_ty.ptrAlignment(zcu).min(elem_ty.abiAlignment(zcu)), |
| 2200 | 2328 | elem_ty, |
| 2201 | | ptr_ty.ptrAlignment(zcu).min(elem_ty.abiAlignment(zcu)).toLlvm(), |
| 2202 | 2329 | if (ptr_ty.isVolatilePtr(zcu)) .@"volatile" else .normal, |
| 2203 | 2330 | ); |
| 2204 | 2331 | } |
| ... | ... | @@ -2294,11 +2421,7 @@ fn airStructFieldVal(self: *FuncGen, inst: Air.Inst.Index) Allocator.Error!Build |
| 2294 | 2421 | const field_ptr = try self.ptraddConst(struct_llvm_val, offset); |
| 2295 | 2422 | const field_ptr_align = struct_ptr_align.offset(offset); |
| 2296 | 2423 | |
| 2297 | | if (isByRef(field_ty, zcu)) { |
| 2298 | | return self.loadByRef(field_ptr, field_ty, field_ptr_align.toLlvm(), .normal); |
| 2299 | | } else { |
| 2300 | | return self.loadTruncate(.normal, field_ty, field_ptr, field_ptr_align.toLlvm()); |
| 2301 | | } |
| 2424 | return self.load(field_ptr, field_ptr_align, field_ty, .normal); |
| 2302 | 2425 | } |
| 2303 | 2426 | |
| 2304 | 2427 | fn airFieldParentPtr(self: *FuncGen, inst: Air.Inst.Index) Allocator.Error!Builder.Value { |
| ... | ... | @@ -2439,8 +2562,8 @@ fn airDbgVarVal(self: *FuncGen, inst: Air.Inst.Index, is_arg: bool) Allocator.Er |
| 2439 | 2562 | // functions even have a valid stack pointer, making the `alloca` + `store` unsafe. |
| 2440 | 2563 | |
| 2441 | 2564 | const alignment = operand_ty.abiAlignment(zcu).toLlvm(); |
| 2442 | | const alloca = try self.buildAlloca(operand.typeOfWip(&self.wip), alignment); |
| 2443 | | _ = try self.wip.store(.normal, operand, alloca, alignment); |
| 2565 | const alloca = try self.buildAlloca(try o.lowerType(operand_ty), alignment); |
| 2566 | try self.store(alloca, .none, operand, operand_ty, .normal); |
| 2444 | 2567 | _ = try self.wip.callIntrinsic( |
| 2445 | 2568 | .normal, |
| 2446 | 2569 | .none, |
| ... | ... | @@ -2609,8 +2732,7 @@ fn airAssembly(self: *FuncGen, inst: Air.Inst.Index) TodoError!Builder.Value { |
| 2609 | 2732 | } else { |
| 2610 | 2733 | const alignment = arg_ty.abiAlignment(zcu).toLlvm(); |
| 2611 | 2734 | const arg_llvm_ty = try o.lowerType(arg_ty); |
| 2612 | | const load_inst = |
| 2613 | | try self.wip.load(.normal, arg_llvm_ty, arg_llvm_value, alignment, ""); |
| 2735 | const load_inst = try self.wip.load(.normal, arg_llvm_ty, arg_llvm_value, alignment, ""); |
| 2614 | 2736 | llvm_param_values[llvm_param_i] = load_inst; |
| 2615 | 2737 | llvm_param_types[llvm_param_i] = arg_llvm_ty; |
| 2616 | 2738 | } |
| ... | ... | @@ -2621,7 +2743,7 @@ fn airAssembly(self: *FuncGen, inst: Air.Inst.Index) TodoError!Builder.Value { |
| 2621 | 2743 | } else { |
| 2622 | 2744 | const alignment = arg_ty.abiAlignment(zcu).toLlvm(); |
| 2623 | 2745 | const arg_ptr = try self.buildAlloca(arg_llvm_value.typeOfWip(&self.wip), alignment); |
| 2624 | | _ = try self.wip.store(.normal, arg_llvm_value, arg_ptr, alignment); |
| 2746 | try self.store(arg_ptr, .none, arg_llvm_value, arg_ty, .normal); |
| 2625 | 2747 | llvm_param_values[llvm_param_i] = arg_ptr; |
| 2626 | 2748 | llvm_param_types[llvm_param_i] = arg_ptr.typeOfWip(&self.wip); |
| 2627 | 2749 | } |
| ... | ... | @@ -2668,14 +2790,8 @@ fn airAssembly(self: *FuncGen, inst: Air.Inst.Index) TodoError!Builder.Value { |
| 2668 | 2790 | llvm_param_values[llvm_param_i] = llvm_rw_vals[output.index]; |
| 2669 | 2791 | llvm_param_types[llvm_param_i] = llvm_rw_vals[output.index].typeOfWip(&self.wip); |
| 2670 | 2792 | } else { |
| 2671 | | const alignment = rw_ty.abiAlignment(zcu).toLlvm(); |
| 2672 | | const loaded = try self.wip.load( |
| 2673 | | if (rw_ty.isVolatilePtr(zcu)) .@"volatile" else .normal, |
| 2674 | | llvm_elem_ty, |
| 2675 | | llvm_rw_vals[output.index], |
| 2676 | | alignment, |
| 2677 | | "", |
| 2678 | | ); |
| 2793 | const access_kind: Builder.MemoryAccessKind = if (rw_ty.isVolatilePtr(zcu)) .@"volatile" else .normal; |
| 2794 | const loaded = try self.load(llvm_rw_vals[output.index], .none, rw_ty.childType(zcu), access_kind); |
| 2679 | 2795 | llvm_param_values[llvm_param_i] = loaded; |
| 2680 | 2796 | llvm_param_types[llvm_param_i] = llvm_elem_ty; |
| 2681 | 2797 | } |
| ... | ... | @@ -2835,12 +2951,12 @@ fn airAssembly(self: *FuncGen, inst: Air.Inst.Index) TodoError!Builder.Value { |
| 2835 | 2951 | if (output != .none) { |
| 2836 | 2952 | const output_ptr = try self.resolveInst(output); |
| 2837 | 2953 | const output_ptr_ty = self.typeOf(output); |
| 2838 | | const alignment = output_ptr_ty.ptrAlignment(zcu).toLlvm(); |
| 2839 | | _ = try self.wip.store( |
| 2840 | | if (output_ptr_ty.isVolatilePtr(zcu)) .@"volatile" else .normal, |
| 2841 | | output_value, |
| 2954 | try self.store( |
| 2842 | 2955 | output_ptr, |
| 2843 | | alignment, |
| 2956 | output_ptr_ty.ptrAlignment(zcu), |
| 2957 | output_value, |
| 2958 | output_ptr_ty.childType(zcu), |
| 2959 | if (output_ptr_ty.isVolatilePtr(zcu)) .@"volatile" else .normal, |
| 2844 | 2960 | ); |
| 2845 | 2961 | } else { |
| 2846 | 2962 | ret_val = output_value; |
| ... | ... | @@ -2863,7 +2979,6 @@ fn airIsNonNull( |
| 2863 | 2979 | const operand = try self.resolveInst(un_op); |
| 2864 | 2980 | const operand_ty = self.typeOf(un_op); |
| 2865 | 2981 | const optional_ty = if (operand_is_ptr) operand_ty.childType(zcu) else operand_ty; |
| 2866 | | const optional_llvm_ty = try o.lowerType(optional_ty); |
| 2867 | 2982 | const payload_ty = optional_ty.optionalChild(zcu); |
| 2868 | 2983 | |
| 2869 | 2984 | const access_kind: Builder.MemoryAccessKind = |
| ... | ... | @@ -2873,7 +2988,7 @@ fn airIsNonNull( |
| 2873 | 2988 | |
| 2874 | 2989 | if (optional_ty.optionalReprIsPayload(zcu)) { |
| 2875 | 2990 | const loaded = if (operand_is_ptr) |
| 2876 | | try self.wip.load(access_kind, optional_llvm_ty, operand, operand_ty.ptrAlignment(zcu).toLlvm(), "") |
| 2991 | try self.load(operand, operand_ty.ptrAlignment(zcu), optional_ty, access_kind) |
| 2877 | 2992 | else |
| 2878 | 2993 | operand; |
| 2879 | 2994 | if (payload_ty.isSlice(zcu)) { |
| ... | ... | @@ -2884,14 +2999,14 @@ fn airIsNonNull( |
| 2884 | 2999 | )); |
| 2885 | 3000 | return self.wip.icmp(cond, slice_ptr, try o.builder.nullValue(ptr_ty), ""); |
| 2886 | 3001 | } |
| 2887 | | return self.wip.icmp(cond, loaded, try o.builder.zeroInitValue(optional_llvm_ty), ""); |
| 3002 | return self.wip.icmp(cond, loaded, try o.builder.zeroInitValue(try o.lowerType(optional_ty)), ""); |
| 2888 | 3003 | } |
| 2889 | 3004 | |
| 2890 | 3005 | comptime assert(optional_layout_version == 3); |
| 2891 | 3006 | |
| 2892 | 3007 | if (!payload_ty.hasRuntimeBits(zcu)) { |
| 2893 | 3008 | const loaded = if (operand_is_ptr) |
| 2894 | | try self.wip.load(access_kind, optional_llvm_ty, operand, operand_ty.ptrAlignment(zcu).toLlvm(), "") |
| 3009 | try self.load(operand, operand_ty.ptrAlignment(zcu), optional_ty, access_kind) |
| 2895 | 3010 | else |
| 2896 | 3011 | operand; |
| 2897 | 3012 | return self.wip.icmp(cond, loaded, try o.builder.intValue(.i8, 0), ""); |
| ... | ... | @@ -2932,7 +3047,7 @@ fn airIsErr( |
| 2932 | 3047 | |
| 2933 | 3048 | if (!payload_ty.hasRuntimeBits(zcu)) { |
| 2934 | 3049 | const loaded = if (operand_is_ptr) |
| 2935 | | try self.wip.load(access_kind, try o.lowerType(err_union_ty), operand, operand_ty.ptrAlignment(zcu).toLlvm(), "") |
| 3050 | try self.load(operand, operand_ty.ptrAlignment(zcu), err_union_ty, access_kind) |
| 2936 | 3051 | else |
| 2937 | 3052 | operand; |
| 2938 | 3053 | return self.wip.icmp(cond, loaded, zero, ""); |
| ... | ... | @@ -2944,7 +3059,7 @@ fn airIsErr( |
| 2944 | 3059 | else |
| 2945 | 3060 | .none; |
| 2946 | 3061 | const err_field_ptr = try self.ptraddConst(operand, codegen.errUnionErrorOffset(payload_ty, zcu)); |
| 2947 | | const loaded = try self.wip.load(access_kind, error_type, err_field_ptr, err_align.toLlvm(), ""); |
| 3062 | const loaded = try self.load(err_field_ptr, err_align, .anyerror, access_kind); |
| 2948 | 3063 | return self.wip.icmp(cond, loaded, zero, ""); |
| 2949 | 3064 | } |
| 2950 | 3065 | |
| ... | ... | @@ -2966,7 +3081,6 @@ fn airOptionalPayloadPtrSet(self: *FuncGen, inst: Air.Inst.Index) Allocator.Erro |
| 2966 | 3081 | const optional_ptr_ty = self.typeOf(ty_op.operand); |
| 2967 | 3082 | const optional_ty = optional_ptr_ty.childType(zcu); |
| 2968 | 3083 | const payload_ty = optional_ty.optionalChild(zcu); |
| 2969 | | const non_null_bit = try o.builder.intValue(.i8, 1); |
| 2970 | 3084 | |
| 2971 | 3085 | const access_kind: Builder.MemoryAccessKind = |
| 2972 | 3086 | if (optional_ptr_ty.isVolatilePtr(zcu)) .@"volatile" else .normal; |
| ... | ... | @@ -2976,7 +3090,7 @@ fn airOptionalPayloadPtrSet(self: *FuncGen, inst: Air.Inst.Index) Allocator.Erro |
| 2976 | 3090 | |
| 2977 | 3091 | // We have a pointer to a i8. We need to set it to 1 and then return the same pointer. |
| 2978 | 3092 | // Default alignment store because align of the non null bit is 1 anyway. |
| 2979 | | _ = try self.wip.store(access_kind, non_null_bit, operand, .default); |
| 3093 | try self.store(operand, .@"1", .true, .bool, access_kind); |
| 2980 | 3094 | return operand; |
| 2981 | 3095 | } |
| 2982 | 3096 | if (optional_ty.optionalReprIsPayload(zcu)) { |
| ... | ... | @@ -2992,7 +3106,7 @@ fn airOptionalPayloadPtrSet(self: *FuncGen, inst: Air.Inst.Index) Allocator.Erro |
| 2992 | 3106 | self.maybeMarkAllowZeroAccess(optional_ptr_ty.ptrInfo(zcu)); |
| 2993 | 3107 | |
| 2994 | 3108 | // Default alignment store because align of the non null bit is 1 anyway. |
| 2995 | | _ = try self.wip.store(access_kind, non_null_bit, non_null_ptr, .default); |
| 3109 | try self.store(non_null_ptr, .@"1", .true, .bool, access_kind); |
| 2996 | 3110 | |
| 2997 | 3111 | // Then return the payload pointer (only if it's used). |
| 2998 | 3112 | if (self.liveness.isUnused(inst)) return .none; |
| ... | ... | @@ -3016,31 +3130,29 @@ fn airOptionalPayload(self: *FuncGen, inst: Air.Inst.Index) Allocator.Error!Buil |
| 3016 | 3130 | return self.optPayloadHandle(operand, optional_ty, false); |
| 3017 | 3131 | } |
| 3018 | 3132 | |
| 3019 | | fn airErrUnionPayload(self: *FuncGen, inst: Air.Inst.Index, operand_is_ptr: bool) Allocator.Error!Builder.Value { |
| 3020 | | const o = self.object; |
| 3133 | fn airErrUnionPayload(fg: *FuncGen, inst: Air.Inst.Index) Allocator.Error!Builder.Value { |
| 3134 | const o = fg.object; |
| 3021 | 3135 | const zcu = o.zcu; |
| 3022 | | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 3023 | | const operand = try self.resolveInst(ty_op.operand); |
| 3024 | | const operand_ty = self.typeOf(ty_op.operand); |
| 3025 | | const err_union_ty = if (operand_is_ptr) operand_ty.childType(zcu) else operand_ty; |
| 3026 | | const result_ty = self.typeOfIndex(inst); |
| 3027 | | const payload_ty = if (operand_is_ptr) result_ty.childType(zcu) else result_ty; |
| 3136 | const ty_op = fg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 3137 | const operand = try fg.resolveInst(ty_op.operand); |
| 3138 | const err_union_ty = fg.typeOf(ty_op.operand); |
| 3139 | const payload_ty = fg.typeOfIndex(inst); |
| 3028 | 3140 | |
| 3029 | | if (!payload_ty.hasRuntimeBits(zcu)) { |
| 3030 | | return if (operand_is_ptr) operand else .none; |
| 3031 | | } |
| 3032 | | const payload_ptr = try self.ptraddConst(operand, codegen.errUnionPayloadOffset(payload_ty, zcu)); |
| 3033 | | if (operand_is_ptr) { |
| 3034 | | return payload_ptr; |
| 3035 | | } |
| 3141 | assert(payload_ty.hasRuntimeBits(zcu)); |
| 3036 | 3142 | assert(isByRef(err_union_ty, zcu)); // error unions are by-ref unless the payload lacks runtime bits |
| 3037 | | const payload_alignment = payload_ty.abiAlignment(zcu).toLlvm(); |
| 3038 | | if (isByRef(payload_ty, zcu)) { |
| 3039 | | return self.loadByRef(payload_ptr, payload_ty, payload_alignment, .normal); |
| 3040 | | } else { |
| 3041 | | const payload_llvm_ty = try o.lowerType(payload_ty); |
| 3042 | | return self.wip.load(.normal, payload_llvm_ty, payload_ptr, payload_alignment, ""); |
| 3043 | | } |
| 3143 | |
| 3144 | const payload_offset = codegen.errUnionPayloadOffset(payload_ty, zcu); |
| 3145 | const payload_ptr = try fg.ptraddConst(operand, payload_offset); |
| 3146 | return fg.load(payload_ptr, err_union_ty.abiAlignment(zcu).offset(payload_offset), payload_ty, .normal); |
| 3147 | } |
| 3148 | |
| 3149 | fn airErrUnionPayloadPtr(fg: *FuncGen, inst: Air.Inst.Index) Allocator.Error!Builder.Value { |
| 3150 | const o = fg.object; |
| 3151 | const zcu = o.zcu; |
| 3152 | const ty_op = fg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 3153 | const operand = try fg.resolveInst(ty_op.operand); |
| 3154 | const payload_ty = fg.typeOfIndex(inst).childType(zcu); |
| 3155 | return fg.ptraddConst(operand, codegen.errUnionPayloadOffset(payload_ty, zcu)); |
| 3044 | 3156 | } |
| 3045 | 3157 | |
| 3046 | 3158 | fn airErrUnionErr( |
| ... | ... | @@ -3053,40 +3165,28 @@ fn airErrUnionErr( |
| 3053 | 3165 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 3054 | 3166 | const operand = try self.resolveInst(ty_op.operand); |
| 3055 | 3167 | const operand_ty = self.typeOf(ty_op.operand); |
| 3056 | | const error_type = try o.errorIntType(); |
| 3057 | 3168 | const err_union_ty = if (operand_is_ptr) operand_ty.childType(zcu) else operand_ty; |
| 3058 | | if (err_union_ty.errorUnionSet(zcu).errorSetIsEmpty(zcu)) { |
| 3059 | | if (operand_is_ptr) { |
| 3060 | | return operand; |
| 3061 | | } else { |
| 3062 | | return o.builder.intValue(error_type, 0); |
| 3063 | | } |
| 3064 | | } |
| 3065 | 3169 | |
| 3066 | 3170 | const access_kind: Builder.MemoryAccessKind = |
| 3067 | 3171 | if (operand_is_ptr and operand_ty.isVolatilePtr(zcu)) .@"volatile" else .normal; |
| 3068 | 3172 | |
| 3069 | 3173 | const payload_ty = err_union_ty.errorUnionPayload(zcu); |
| 3070 | | if (!payload_ty.hasRuntimeBits(zcu)) { |
| 3071 | | if (!operand_is_ptr) return operand; |
| 3072 | | |
| 3073 | | self.maybeMarkAllowZeroAccess(operand_ty.ptrInfo(zcu)); |
| 3074 | 3174 | |
| 3075 | | return self.wip.load(access_kind, error_type, operand, operand_ty.ptrAlignment(zcu).toLlvm(), ""); |
| 3175 | if (payload_ty.hasRuntimeBits(zcu)) { |
| 3176 | assert(isByRef(err_union_ty, zcu)); // error unions are by-ref unless the payload lacks runtime bits |
| 3177 | } else if (!operand_is_ptr) { |
| 3178 | return operand; |
| 3076 | 3179 | } |
| 3077 | 3180 | |
| 3078 | | assert(isByRef(err_union_ty, zcu)); // error unions are by-ref unless the payload lacks runtime bits |
| 3079 | | |
| 3080 | 3181 | if (operand_is_ptr) self.maybeMarkAllowZeroAccess(operand_ty.ptrInfo(zcu)); |
| 3081 | 3182 | |
| 3082 | | const err_align: InternPool.Alignment = a: { |
| 3083 | | const err_abi_align = Type.anyerror.abiAlignment(zcu); |
| 3084 | | if (!operand_is_ptr) break :a err_abi_align; |
| 3085 | | break :a err_abi_align.minStrict(operand_ty.ptrAlignment(zcu)); |
| 3086 | | }; |
| 3183 | const ptr_align = if (operand_is_ptr) operand_ty.ptrAlignment(zcu) else err_union_ty.abiAlignment(zcu); |
| 3087 | 3184 | |
| 3088 | | const err_field_ptr = try self.ptraddConst(operand, codegen.errUnionErrorOffset(payload_ty, zcu)); |
| 3089 | | return self.wip.load(access_kind, error_type, err_field_ptr, err_align.toLlvm(), ""); |
| 3185 | const err_offset = codegen.errUnionErrorOffset(payload_ty, zcu); |
| 3186 | const err_align = ptr_align.offset(err_offset); |
| 3187 | const err_ptr = try self.ptraddConst(operand, err_offset); |
| 3188 | |
| 3189 | return self.load(err_ptr, err_align, .anyerror, access_kind); |
| 3090 | 3190 | } |
| 3091 | 3191 | |
| 3092 | 3192 | fn airErrUnionPayloadPtrSet(self: *FuncGen, inst: Air.Inst.Index) Allocator.Error!Builder.Value { |
| ... | ... | @@ -3107,10 +3207,10 @@ fn airErrUnionPayloadPtrSet(self: *FuncGen, inst: Air.Inst.Index) Allocator.Erro |
| 3107 | 3207 | self.maybeMarkAllowZeroAccess(err_union_ptr_ty.ptrInfo(zcu)); |
| 3108 | 3208 | |
| 3109 | 3209 | { |
| 3110 | | const error_align = Type.anyerror.abiAlignment(zcu).minStrict(err_union_ptr_align).toLlvm(); |
| 3111 | 3210 | // First set the non-error value. |
| 3112 | | const error_ptr = try self.ptraddConst(operand, codegen.errUnionErrorOffset(payload_ty, zcu)); |
| 3113 | | _ = try self.wip.store(access_kind, non_error_val, error_ptr, error_align); |
| 3211 | const error_off = codegen.errUnionErrorOffset(payload_ty, zcu); |
| 3212 | const error_ptr = try self.ptraddConst(operand, error_off); |
| 3213 | try self.store(error_ptr, err_union_ptr_align.offset(error_off), non_error_val, .anyerror, access_kind); |
| 3114 | 3214 | } |
| 3115 | 3215 | |
| 3116 | 3216 | // Then return the payload pointer (only if it is used). |
| ... | ... | @@ -3142,7 +3242,7 @@ fn airSaveErrReturnTraceIndex(self: *FuncGen, inst: Air.Inst.Index) Allocator.Er |
| 3142 | 3242 | const field_offset = struct_ty.structFieldOffset(field_index, zcu); |
| 3143 | 3243 | const field_align = struct_ty.abiAlignment(zcu).offset(field_offset); |
| 3144 | 3244 | const field_ptr = try self.ptraddConst(self.err_ret_trace, field_offset); |
| 3145 | | return self.load(field_ptr, field_ty, field_align.toLlvm(), .normal); |
| 3245 | return self.load(field_ptr, field_align, field_ty, .normal); |
| 3146 | 3246 | } |
| 3147 | 3247 | |
| 3148 | 3248 | /// As an optimization, we want to avoid unnecessary copies of |
| ... | ... | @@ -3174,7 +3274,6 @@ fn airWrapOptional(self: *FuncGen, body_tail: []const Air.Inst.Index) Allocator. |
| 3174 | 3274 | const inst = body_tail[0]; |
| 3175 | 3275 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 3176 | 3276 | const payload_ty = self.typeOf(ty_op.operand); |
| 3177 | | const non_null_bit = try o.builder.intValue(.i8, 1); |
| 3178 | 3277 | comptime assert(optional_layout_version == 3); |
| 3179 | 3278 | assert(payload_ty.hasRuntimeBits(zcu)); |
| 3180 | 3279 | const operand = try self.resolveInst(ty_op.operand); |
| ... | ... | @@ -3191,15 +3290,12 @@ fn airWrapOptional(self: *FuncGen, body_tail: []const Air.Inst.Index) Allocator. |
| 3191 | 3290 | }; |
| 3192 | 3291 | |
| 3193 | 3292 | const payload_ptr = optional_ptr; // payload always at offset 0 |
| 3194 | | try self.store( |
| 3195 | | payload_ptr, |
| 3196 | | .none, |
| 3197 | | operand, |
| 3198 | | payload_ty, |
| 3199 | | ); |
| 3293 | try self.store(payload_ptr, .none, operand, payload_ty, .normal); |
| 3294 | |
| 3200 | 3295 | // Non-null bit immediately after payload (no padding because the bit has alignment 1). |
| 3201 | 3296 | const non_null_ptr = try self.ptraddConst(optional_ptr, payload_ty.abiSize(zcu)); |
| 3202 | | _ = try self.wip.store(.normal, non_null_bit, non_null_ptr, .default); |
| 3297 | try self.store(non_null_ptr, .none, .true, .bool, .normal); |
| 3298 | |
| 3203 | 3299 | return optional_ptr; |
| 3204 | 3300 | } |
| 3205 | 3301 | |
| ... | ... | @@ -3225,15 +3321,11 @@ fn airWrapErrUnionPayload(self: *FuncGen, body_tail: []const Air.Inst.Index) All |
| 3225 | 3321 | }; |
| 3226 | 3322 | |
| 3227 | 3323 | const err_ptr = try self.ptraddConst(result_ptr, codegen.errUnionErrorOffset(payload_ty, zcu)); |
| 3228 | | const error_alignment = Type.anyerror.abiAlignment(o.zcu).toLlvm(); |
| 3229 | | _ = try self.wip.store(.normal, ok_err_code, err_ptr, error_alignment); |
| 3324 | try self.store(err_ptr, .none, ok_err_code, .anyerror, .normal); |
| 3325 | |
| 3230 | 3326 | const payload_ptr = try self.ptraddConst(result_ptr, codegen.errUnionPayloadOffset(payload_ty, zcu)); |
| 3231 | | try self.store( |
| 3232 | | payload_ptr, |
| 3233 | | .none, |
| 3234 | | operand, |
| 3235 | | payload_ty, |
| 3236 | | ); |
| 3327 | try self.store(payload_ptr, .none, operand, payload_ty, .normal); |
| 3328 | |
| 3237 | 3329 | return result_ptr; |
| 3238 | 3330 | } |
| 3239 | 3331 | |
| ... | ... | @@ -3258,11 +3350,12 @@ fn airWrapErrUnionErr(self: *FuncGen, body_tail: []const Air.Inst.Index) Allocat |
| 3258 | 3350 | }; |
| 3259 | 3351 | |
| 3260 | 3352 | const err_ptr = try self.ptraddConst(result_ptr, codegen.errUnionErrorOffset(payload_ty, zcu)); |
| 3261 | | const error_alignment = Type.anyerror.abiAlignment(zcu).toLlvm(); |
| 3262 | | _ = try self.wip.store(.normal, operand, err_ptr, error_alignment); |
| 3353 | try self.store(err_ptr, .none, operand, .anyerror, .normal); |
| 3354 | |
| 3263 | 3355 | const payload_ptr = try self.ptraddConst(result_ptr, codegen.errUnionPayloadOffset(payload_ty, zcu)); |
| 3264 | 3356 | // TODO store undef to payload_ptr |
| 3265 | 3357 | _ = payload_ptr; |
| 3358 | |
| 3266 | 3359 | return result_ptr; |
| 3267 | 3360 | } |
| 3268 | 3361 | |
| ... | ... | @@ -3723,19 +3816,21 @@ fn airOverflow( |
| 3723 | 3816 | const result_val = try self.wip.extractValue(results, &.{0}, ""); |
| 3724 | 3817 | const overflow_bit = try self.wip.extractValue(results, &.{1}, ""); |
| 3725 | 3818 | |
| 3726 | | const result_alignment = inst_ty.abiAlignment(zcu).toLlvm(); |
| 3727 | | const alloca_inst = try self.buildAlloca(llvm_inst_ty, result_alignment); |
| 3819 | const result_alignment = inst_ty.abiAlignment(zcu); |
| 3820 | const alloca_inst = try self.buildAlloca(llvm_inst_ty, result_alignment.toLlvm()); |
| 3728 | 3821 | |
| 3729 | 3822 | { |
| 3730 | 3823 | // Store to 'result: IntType' field |
| 3731 | | const field_ptr = try self.ptraddConst(alloca_inst, inst_ty.structFieldOffset(0, zcu)); |
| 3732 | | _ = try self.wip.store(.normal, result_val, field_ptr, lhs_ty.abiAlignment(zcu).toLlvm()); |
| 3824 | const field_off = inst_ty.structFieldOffset(0, zcu); |
| 3825 | const field_ptr = try self.ptraddConst(alloca_inst, field_off); |
| 3826 | try self.store(field_ptr, result_alignment.offset(field_off), result_val, lhs_ty, .normal); |
| 3733 | 3827 | } |
| 3734 | 3828 | |
| 3735 | 3829 | { |
| 3736 | 3830 | // Store to 'overflow: u1' field |
| 3737 | | const field_ptr = try self.ptraddConst(alloca_inst, inst_ty.structFieldOffset(1, zcu)); |
| 3738 | | _ = try self.wip.store(.normal, overflow_bit, field_ptr, comptime .fromByteUnits(1)); |
| 3831 | const field_off = inst_ty.structFieldOffset(1, zcu); |
| 3832 | const field_ptr = try self.ptraddConst(alloca_inst, field_off); |
| 3833 | try self.store(field_ptr, result_alignment.offset(field_off), overflow_bit, inst_ty.fieldType(1, zcu), .normal); |
| 3739 | 3834 | } |
| 3740 | 3835 | |
| 3741 | 3836 | return alloca_inst; |
| ... | ... | @@ -4064,19 +4159,21 @@ fn airShlWithOverflow(self: *FuncGen, inst: Air.Inst.Index) Allocator.Error!Buil |
| 4064 | 4159 | |
| 4065 | 4160 | const overflow_bit = try self.wip.icmp(.ne, lhs, reconstructed, ""); |
| 4066 | 4161 | |
| 4067 | | const result_alignment = dest_ty.abiAlignment(zcu).toLlvm(); |
| 4068 | | const alloca_inst = try self.buildAlloca(llvm_dest_ty, result_alignment); |
| 4162 | const result_alignment = dest_ty.abiAlignment(zcu); |
| 4163 | const alloca_inst = try self.buildAlloca(llvm_dest_ty, result_alignment.toLlvm()); |
| 4069 | 4164 | |
| 4070 | 4165 | { |
| 4071 | 4166 | // Store to 'result: IntType' field |
| 4072 | | const field_ptr = try self.ptraddConst(alloca_inst, dest_ty.structFieldOffset(0, zcu)); |
| 4073 | | _ = try self.wip.store(.normal, result, field_ptr, lhs_ty.abiAlignment(zcu).toLlvm()); |
| 4167 | const field_off = dest_ty.structFieldOffset(0, zcu); |
| 4168 | const field_ptr = try self.ptraddConst(alloca_inst, field_off); |
| 4169 | try self.store(field_ptr, result_alignment.offset(field_off), result, lhs_ty, .normal); |
| 4074 | 4170 | } |
| 4075 | 4171 | |
| 4076 | 4172 | { |
| 4077 | 4173 | // Store to 'overflow: u1' field |
| 4078 | | const field_ptr = try self.ptraddConst(alloca_inst, dest_ty.structFieldOffset(1, zcu)); |
| 4079 | | _ = try self.wip.store(.normal, overflow_bit, field_ptr, comptime .fromByteUnits(1)); |
| 4174 | const field_off = dest_ty.structFieldOffset(1, zcu); |
| 4175 | const field_ptr = try self.ptraddConst(alloca_inst, field_off); |
| 4176 | try self.store(field_ptr, result_alignment.offset(field_off), overflow_bit, dest_ty.fieldType(1, zcu), .normal); |
| 4080 | 4177 | } |
| 4081 | 4178 | |
| 4082 | 4179 | return alloca_inst; |
| ... | ... | @@ -4264,7 +4361,7 @@ fn airAbs(self: *FuncGen, inst: Air.Inst.Index) Allocator.Error!Builder.Value { |
| 4264 | 4361 | .none, |
| 4265 | 4362 | .abs, |
| 4266 | 4363 | &.{try o.lowerType(operand_ty)}, |
| 4267 | | &.{ operand, try o.builder.intValue(.i1, 0) }, |
| 4364 | &.{ operand, .false }, |
| 4268 | 4365 | "", |
| 4269 | 4366 | ), |
| 4270 | 4367 | .float => return self.buildFloatOp(.fabs, .normal, operand_ty, 1, .{operand}), |
| ... | ... | @@ -4483,7 +4580,8 @@ fn bitCast(self: *FuncGen, operand: Builder.Value, operand_ty: Type, inst_ty: Ty |
| 4483 | 4580 | } |
| 4484 | 4581 | |
| 4485 | 4582 | if (inst_ty.isAbiInt(zcu) and operand_ty.isAbiInt(zcu)) { |
| 4486 | | return self.wip.conv(.unsigned, operand, llvm_dest_ty, ""); |
| 4583 | assert(inst_ty.bitSize(zcu) == operand_ty.bitSize(zcu)); |
| 4584 | return operand; |
| 4487 | 4585 | } |
| 4488 | 4586 | |
| 4489 | 4587 | const operand_scalar_ty = operand_ty.scalarType(zcu); |
| ... | ... | @@ -4498,11 +4596,11 @@ fn bitCast(self: *FuncGen, operand: Builder.Value, operand_ty: Type, inst_ty: Ty |
| 4498 | 4596 | if (operand_ty.zigTypeTag(zcu) == .vector and inst_ty.zigTypeTag(zcu) == .array) { |
| 4499 | 4597 | const elem_ty = operand_scalar_ty; |
| 4500 | 4598 | assert(result_is_ref); // arrays are always by-ref provided they have runtime bits |
| 4501 | | const alignment = inst_ty.abiAlignment(zcu).toLlvm(); |
| 4502 | | const array_ptr = try self.buildAlloca(llvm_dest_ty, alignment); |
| 4599 | const alignment = inst_ty.abiAlignment(zcu); |
| 4600 | const array_ptr = try self.buildAlloca(llvm_dest_ty, alignment.toLlvm()); |
| 4503 | 4601 | const bitcast_ok = elem_ty.bitSize(zcu) == elem_ty.abiSize(zcu) * 8; |
| 4504 | 4602 | if (bitcast_ok) { |
| 4505 | | _ = try self.wip.store(.normal, operand, array_ptr, alignment); |
| 4603 | try self.store(array_ptr, alignment, operand, operand_ty, .normal); |
| 4506 | 4604 | } else { |
| 4507 | 4605 | // If the ABI size of the element type is not evenly divisible by size in bits; |
| 4508 | 4606 | // a simple bitcast will not work, and we fall back to extractelement. |
| ... | ... | @@ -4512,7 +4610,7 @@ fn bitCast(self: *FuncGen, operand: Builder.Value, operand_ty: Type, inst_ty: Ty |
| 4512 | 4610 | while (i < vector_len) : (i += 1) { |
| 4513 | 4611 | const arr_elem_ptr = try self.ptraddConst(array_ptr, i * elem_size); |
| 4514 | 4612 | const vec_elem = try self.wip.extractElement(operand, try o.builder.intValue(.i32, i), ""); |
| 4515 | | _ = try self.wip.store(.normal, vec_elem, arr_elem_ptr, .default); |
| 4613 | try self.store(arr_elem_ptr, .none, vec_elem, elem_ty, .normal); |
| 4516 | 4614 | } |
| 4517 | 4615 | } |
| 4518 | 4616 | return array_ptr; |
| ... | ... | @@ -4525,19 +4623,17 @@ fn bitCast(self: *FuncGen, operand: Builder.Value, operand_ty: Type, inst_ty: Ty |
| 4525 | 4623 | if (bitcast_ok) { |
| 4526 | 4624 | // The array is aligned to the element's alignment, while the vector might have a completely |
| 4527 | 4625 | // different alignment. This means we need to enforce the alignment of this load. |
| 4528 | | const alignment = elem_ty.abiAlignment(zcu).toLlvm(); |
| 4529 | | return self.wip.load(.normal, llvm_vector_ty, operand, alignment, ""); |
| 4626 | return self.load(operand, elem_ty.abiAlignment(zcu), inst_ty, .normal); |
| 4530 | 4627 | } else { |
| 4531 | 4628 | // If the ABI size of the element type is not evenly divisible by size in bits; |
| 4532 | 4629 | // a simple bitcast will not work, and we fall back to extractelement. |
| 4533 | | const elem_llvm_ty = try o.lowerType(elem_ty); |
| 4534 | 4630 | const elem_size = elem_ty.abiSize(zcu); |
| 4535 | 4631 | const vector_len = operand_ty.arrayLen(zcu); |
| 4536 | 4632 | var vector = try o.builder.poisonValue(llvm_vector_ty); |
| 4537 | 4633 | var i: u64 = 0; |
| 4538 | 4634 | while (i < vector_len) : (i += 1) { |
| 4539 | 4635 | const arr_elem_ptr = try self.ptraddConst(operand, i * elem_size); |
| 4540 | | const arr_elem = try self.wip.load(.normal, elem_llvm_ty, arr_elem_ptr, .default, ""); |
| 4636 | const arr_elem = try self.load(arr_elem_ptr, .none, elem_ty, .normal); |
| 4541 | 4637 | vector = try self.wip.insertElement(vector, arr_elem, try o.builder.intValue(.i32, i), ""); |
| 4542 | 4638 | } |
| 4543 | 4639 | return vector; |
| ... | ... | @@ -4545,14 +4641,17 @@ fn bitCast(self: *FuncGen, operand: Builder.Value, operand_ty: Type, inst_ty: Ty |
| 4545 | 4641 | } |
| 4546 | 4642 | |
| 4547 | 4643 | if (operand_is_ref) { |
| 4548 | | const alignment = operand_ty.abiAlignment(zcu).toLlvm(); |
| 4549 | | return self.wip.load(.normal, llvm_dest_ty, operand, alignment, ""); |
| 4644 | return self.load(operand, operand_ty.abiAlignment(zcu), inst_ty, .normal); |
| 4550 | 4645 | } |
| 4551 | 4646 | |
| 4552 | 4647 | if (result_is_ref) { |
| 4553 | | const alignment = operand_ty.abiAlignment(zcu).max(inst_ty.abiAlignment(zcu)).toLlvm(); |
| 4554 | | const result_ptr = try self.buildAlloca(llvm_dest_ty, alignment); |
| 4555 | | _ = try self.wip.store(.normal, operand, result_ptr, alignment); |
| 4648 | const alignment = operand_ty.abiAlignment(zcu).max(inst_ty.abiAlignment(zcu)); |
| 4649 | const llvm_alloc_ty = if (operand_ty.abiSize(zcu) > inst_ty.abiSize(zcu)) |
| 4650 | try o.lowerType(operand_ty) |
| 4651 | else |
| 4652 | llvm_dest_ty; |
| 4653 | const result_ptr = try self.buildAlloca(llvm_alloc_ty, alignment.toLlvm()); |
| 4654 | try self.store(result_ptr, alignment, operand, operand_ty, .normal); |
| 4556 | 4655 | return result_ptr; |
| 4557 | 4656 | } |
| 4558 | 4657 | |
| ... | ... | @@ -4563,10 +4662,10 @@ fn bitCast(self: *FuncGen, operand: Builder.Value, operand_ty: Type, inst_ty: Ty |
| 4563 | 4662 | // Both our operand and our result are values, not pointers, |
| 4564 | 4663 | // but LLVM won't let us bitcast struct values or vectors with padding bits. |
| 4565 | 4664 | // Therefore, we store operand to alloca, then load for result. |
| 4566 | | const alignment = operand_ty.abiAlignment(zcu).max(inst_ty.abiAlignment(zcu)).toLlvm(); |
| 4567 | | const result_ptr = try self.buildAlloca(llvm_dest_ty, alignment); |
| 4568 | | _ = try self.wip.store(.normal, operand, result_ptr, alignment); |
| 4569 | | return self.wip.load(.normal, llvm_dest_ty, result_ptr, alignment, ""); |
| 4665 | const alignment = operand_ty.abiAlignment(zcu).max(inst_ty.abiAlignment(zcu)); |
| 4666 | const result_ptr = try self.buildAlloca(llvm_dest_ty, alignment.toLlvm()); |
| 4667 | try self.store(result_ptr, alignment, operand, operand_ty, .normal); |
| 4668 | return self.load(result_ptr, alignment, inst_ty, .normal); |
| 4570 | 4669 | } |
| 4571 | 4670 | |
| 4572 | 4671 | return self.wip.cast(.bitcast, operand, llvm_dest_ty, ""); |
| ... | ... | @@ -4629,8 +4728,8 @@ fn airArg(self: *FuncGen, inst: Air.Inst.Index) Allocator.Error!Builder.Value { |
| 4629 | 4728 | ); |
| 4630 | 4729 | } else if (mod.optimize_mode == .Debug) { |
| 4631 | 4730 | const alignment = inst_ty.abiAlignment(zcu).toLlvm(); |
| 4632 | | const alloca = try self.buildAlloca(arg_val.typeOfWip(&self.wip), alignment); |
| 4633 | | _ = try self.wip.store(.normal, arg_val, alloca, alignment); |
| 4731 | const alloca = try self.buildAlloca(try o.lowerType(inst_ty), alignment); |
| 4732 | try self.store(alloca, .none, arg_val, inst_ty, .normal); |
| 4634 | 4733 | _ = try self.wip.callIntrinsic( |
| 4635 | 4734 | .normal, |
| 4636 | 4735 | .none, |
| ... | ... | @@ -4689,28 +4788,56 @@ fn airRetPtr(self: *FuncGen, inst: Air.Inst.Index) Allocator.Error!Builder.Value |
| 4689 | 4788 | return self.buildAlloca(llvm_elem_ty, ptr_align.toLlvm()); |
| 4690 | 4789 | } |
| 4691 | 4790 | |
| 4692 | | /// Use this instead of builder.buildAlloca, because this function makes sure to |
| 4693 | | /// put the alloca instruction at the top of the function! |
| 4791 | /// Unlike `WipFunction.alloca`, this puts the alloca instruction at the top of the function. |
| 4694 | 4792 | fn buildAlloca( |
| 4695 | | self: *FuncGen, |
| 4793 | fg: *FuncGen, |
| 4696 | 4794 | llvm_ty: Builder.Type, |
| 4697 | 4795 | alignment: Builder.Alignment, |
| 4698 | 4796 | ) Allocator.Error!Builder.Value { |
| 4699 | | const target = self.object.zcu.getTarget(); |
| 4700 | | return buildAllocaInner(&self.wip, llvm_ty, alignment, target); |
| 4797 | const wip = &fg.wip; |
| 4798 | |
| 4799 | const alloca = blk: { |
| 4800 | const prev_cursor = wip.cursor; |
| 4801 | const prev_debug_location = wip.debug_location; |
| 4802 | defer { |
| 4803 | wip.cursor = prev_cursor; |
| 4804 | if (wip.cursor.block == .entry) wip.cursor.instruction += 1; |
| 4805 | wip.debug_location = prev_debug_location; |
| 4806 | } |
| 4807 | |
| 4808 | wip.cursor = .{ .block = .entry }; |
| 4809 | wip.debug_location = .no_location; |
| 4810 | const address_space = llvmAllocaAddressSpace(fg.object.zcu.getTarget()); |
| 4811 | break :blk try wip.alloca(.normal, llvm_ty, .none, alignment, address_space, ""); |
| 4812 | }; |
| 4813 | |
| 4814 | // The pointer returned from this function should have the generic address space, |
| 4815 | // if this isn't the case then cast it to the generic address space. |
| 4816 | return fg.wip.conv(.unneeded, alloca, .ptr, ""); |
| 4701 | 4817 | } |
| 4702 | 4818 | |
| 4703 | | fn airStore(self: *FuncGen, inst: Air.Inst.Index, safety: bool) Allocator.Error!Builder.Value { |
| 4704 | | const o = self.object; |
| 4819 | fn airStore(fg: *FuncGen, inst: Air.Inst.Index, safety: bool) Allocator.Error!Builder.Value { |
| 4820 | const o = fg.object; |
| 4705 | 4821 | const zcu = o.zcu; |
| 4706 | | const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; |
| 4707 | | const dest_ptr = try self.resolveInst(bin_op.lhs); |
| 4708 | | const ptr_ty = self.typeOf(bin_op.lhs); |
| 4709 | | const operand_ty = ptr_ty.childType(zcu); |
| 4822 | const bin_op = fg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; |
| 4823 | const ptr = try fg.resolveInst(bin_op.lhs); |
| 4824 | const ptr_ty = fg.typeOf(bin_op.lhs); |
| 4825 | const ptr_info = ptr_ty.ptrInfo(zcu); |
| 4826 | const ptr_alignment = ptr_ty.ptrAlignment(zcu); |
| 4827 | |
| 4828 | const elem_ty = fg.typeOf(bin_op.rhs); |
| 4829 | assert(elem_ty.hasRuntimeBits(zcu)); |
| 4830 | |
| 4831 | fg.maybeMarkAllowZeroAccess(ptr_info); |
| 4832 | |
| 4833 | const access_kind: Builder.MemoryAccessKind = switch (ptr_info.flags.is_volatile) { |
| 4834 | true => .@"volatile", |
| 4835 | false => .normal, |
| 4836 | }; |
| 4710 | 4837 | |
| 4711 | 4838 | const val_is_undef = if (bin_op.rhs.toInterned()) |i| Value.fromInterned(i).isUndef(zcu) else false; |
| 4712 | | if (val_is_undef and !self.needMemsetWorkaround(operand_ty.abiSize(zcu))) { |
| 4713 | | const owner_mod = self.ownerModule(); |
| 4839 | if (val_is_undef and !fg.needMemsetWorkaround(elem_ty.abiSize(zcu))) { |
| 4840 | const owner_mod = fg.ownerModule(); |
| 4714 | 4841 | |
| 4715 | 4842 | // Even if safety is disabled, we still emit a memset to undefined since it conveys |
| 4716 | 4843 | // extra information to LLVM, and LLVM will optimize it out. Safety makes the difference |
| ... | ... | @@ -4725,7 +4852,6 @@ fn airStore(self: *FuncGen, inst: Air.Inst.Index, safety: bool) Allocator.Error! |
| 4725 | 4852 | return .none; |
| 4726 | 4853 | } |
| 4727 | 4854 | |
| 4728 | | const ptr_info = ptr_ty.ptrInfo(zcu); |
| 4729 | 4855 | const needs_bitmask = (ptr_info.packed_offset.host_size != 0); |
| 4730 | 4856 | if (needs_bitmask) { |
| 4731 | 4857 | // TODO: only some bits are to be undef, we cannot write with a simple memset. |
| ... | ... | @@ -4734,27 +4860,82 @@ fn airStore(self: *FuncGen, inst: Air.Inst.Index, safety: bool) Allocator.Error! |
| 4734 | 4860 | return .none; |
| 4735 | 4861 | } |
| 4736 | 4862 | |
| 4737 | | self.maybeMarkAllowZeroAccess(ptr_info); |
| 4738 | | |
| 4739 | | const len = try o.builder.intValue(try o.lowerType(.usize), operand_ty.abiSize(zcu)); |
| 4740 | | _ = try self.wip.callMemSet( |
| 4741 | | dest_ptr, |
| 4742 | | ptr_ty.ptrAlignment(zcu).toLlvm(), |
| 4863 | const len = try o.builder.intValue(try o.lowerType(.usize), elem_ty.abiSize(zcu)); |
| 4864 | _ = try fg.wip.callMemSet( |
| 4865 | ptr, |
| 4866 | ptr_alignment.toLlvm(), |
| 4743 | 4867 | if (safety) try o.builder.intValue(.i8, 0xaa) else try o.builder.undefValue(.i8), |
| 4744 | 4868 | len, |
| 4745 | | if (ptr_ty.isVolatilePtr(zcu)) .@"volatile" else .normal, |
| 4746 | | self.disable_intrinsics, |
| 4869 | access_kind, |
| 4870 | fg.disable_intrinsics, |
| 4747 | 4871 | ); |
| 4748 | 4872 | if (safety and owner_mod.valgrind) { |
| 4749 | | try self.valgrindMarkUndef(dest_ptr, len); |
| 4873 | try fg.valgrindMarkUndef(ptr, len); |
| 4750 | 4874 | } |
| 4751 | 4875 | return .none; |
| 4752 | 4876 | } |
| 4753 | 4877 | |
| 4754 | | self.maybeMarkAllowZeroAccess(ptr_ty.ptrInfo(zcu)); |
| 4878 | const elem = try fg.resolveInst(bin_op.rhs); |
| 4879 | |
| 4880 | if (ptr_info.flags.vector_index != .none) { |
| 4881 | // Accepted proposal https://github.com/ziglang/zig/issues/24061 will eliminate this usage of `pt`. |
| 4882 | const vec_ty = try fg.pt.vectorType(.{ |
| 4883 | .len = ptr_info.packed_offset.host_size, |
| 4884 | .child = elem_ty.toIntern(), |
| 4885 | }); |
| 4886 | |
| 4887 | const loaded_vector = try fg.load(ptr, ptr_alignment, vec_ty, access_kind); |
| 4888 | const index_val = try o.builder.intValue(.i32, ptr_info.flags.vector_index); |
| 4889 | const modified_vector = try fg.wip.insertElement(loaded_vector, elem, index_val, ""); |
| 4890 | |
| 4891 | try fg.store(ptr, ptr_alignment, modified_vector, vec_ty, access_kind); |
| 4892 | return .none; |
| 4893 | } |
| 4894 | |
| 4895 | if (ptr_info.packed_offset.host_size != 0) { |
| 4896 | // Accepted proposal https://github.com/ziglang/zig/issues/24061 will eliminate this usage of `pt`. |
| 4897 | const backing_int_ty = try fg.pt.intType(.unsigned, @intCast(ptr_info.packed_offset.host_size * 8)); |
| 4898 | const llvm_backing_int_ty = try o.lowerType(backing_int_ty); |
| 4899 | |
| 4900 | const backing_int_val = try fg.load(ptr, ptr_alignment, backing_int_ty, access_kind); |
| 4755 | 4901 | |
| 4756 | | const src_operand = try self.resolveInst(bin_op.rhs); |
| 4757 | | try self.storeFull(dest_ptr, ptr_ty, src_operand, .none); |
| 4902 | const elem_bits = ptr_ty.childType(zcu).bitSize(zcu); |
| 4903 | const shift_amt = try o.builder.intConst(llvm_backing_int_ty, ptr_info.packed_offset.bit_offset); |
| 4904 | |
| 4905 | // Convert to equally-sized integer type in order to perform the bit |
| 4906 | // operations on the value to store |
| 4907 | const new_val_bits_type = try o.builder.intType(@intCast(elem_bits)); |
| 4908 | const new_val_bits = if (elem_ty.isPtrAtRuntime(zcu)) |
| 4909 | try fg.wip.cast(.ptrtoint, elem, new_val_bits_type, "") |
| 4910 | else |
| 4911 | try fg.wip.cast(.bitcast, elem, new_val_bits_type, ""); |
| 4912 | |
| 4913 | const mask_val = blk: { |
| 4914 | const zext = try fg.wip.cast( |
| 4915 | .zext, |
| 4916 | try o.builder.intValue(new_val_bits_type, -1), |
| 4917 | llvm_backing_int_ty, |
| 4918 | "", |
| 4919 | ); |
| 4920 | const shl = try fg.wip.bin(.shl, zext, shift_amt.toValue(), ""); |
| 4921 | break :blk try fg.wip.bin( |
| 4922 | .xor, |
| 4923 | shl, |
| 4924 | try o.builder.intValue(llvm_backing_int_ty, -1), |
| 4925 | "", |
| 4926 | ); |
| 4927 | }; |
| 4928 | |
| 4929 | const masked_backing_int_val = try fg.wip.bin(.@"and", backing_int_val, mask_val, ""); |
| 4930 | const extended_new_val = try fg.wip.cast(.zext, new_val_bits, llvm_backing_int_ty, ""); |
| 4931 | const shifted_new_val = try fg.wip.bin(.shl, extended_new_val, shift_amt.toValue(), ""); |
| 4932 | const new_backing_int_val = try fg.wip.bin(.@"or", shifted_new_val, masked_backing_int_val, ""); |
| 4933 | |
| 4934 | try fg.store(ptr, ptr_alignment, new_backing_int_val, backing_int_ty, access_kind); |
| 4935 | return .none; |
| 4936 | } |
| 4937 | |
| 4938 | try fg.store(ptr, ptr_alignment, elem, elem_ty, access_kind); |
| 4758 | 4939 | return .none; |
| 4759 | 4940 | } |
| 4760 | 4941 | |
| ... | ... | @@ -4766,7 +4947,7 @@ fn airLoad(fg: *FuncGen, inst: Air.Inst.Index) Allocator.Error!Builder.Value { |
| 4766 | 4947 | const ptr_info = ptr_ty.ptrInfo(zcu); |
| 4767 | 4948 | const ptr = try fg.resolveInst(ty_op.operand); |
| 4768 | 4949 | const elem_ty = ptr_ty.childType(zcu); |
| 4769 | | const llvm_ptr_align = ptr_ty.ptrAlignment(zcu).toLlvm(); |
| 4950 | const ptr_align = ptr_ty.ptrAlignment(zcu); |
| 4770 | 4951 | |
| 4771 | 4952 | fg.maybeMarkAllowZeroAccess(ptr_info); |
| 4772 | 4953 | |
| ... | ... | @@ -4774,36 +4955,32 @@ fn airLoad(fg: *FuncGen, inst: Air.Inst.Index) Allocator.Error!Builder.Value { |
| 4774 | 4955 | if (ptr_info.flags.is_volatile) .@"volatile" else .normal; |
| 4775 | 4956 | |
| 4776 | 4957 | if (ptr_info.flags.vector_index != .none) { |
| 4777 | | const index_u32 = try o.builder.intValue(.i32, ptr_info.flags.vector_index); |
| 4778 | | const vec_elem_ty = try o.lowerType(elem_ty); |
| 4779 | | const vec_ty = try o.builder.vectorType(.normal, ptr_info.packed_offset.host_size, vec_elem_ty); |
| 4780 | | |
| 4781 | | const loaded_vector = try fg.wip.load(access_kind, vec_ty, ptr, llvm_ptr_align, ""); |
| 4782 | | return fg.wip.extractElement(loaded_vector, index_u32, ""); |
| 4958 | // Accepted proposal https://github.com/ziglang/zig/issues/24061 will eliminate this usage of `pt`. |
| 4959 | const vec_ty = try fg.pt.vectorType(.{ |
| 4960 | .len = ptr_info.packed_offset.host_size, |
| 4961 | .child = elem_ty.toIntern(), |
| 4962 | }); |
| 4963 | const vector_val = try fg.load(ptr, ptr_align, vec_ty, access_kind); |
| 4964 | const index_val = try o.builder.intValue(.i32, ptr_info.flags.vector_index); |
| 4965 | return fg.wip.extractElement(vector_val, index_val, ""); |
| 4783 | 4966 | } |
| 4784 | 4967 | |
| 4785 | 4968 | if (ptr_info.packed_offset.host_size == 0) { |
| 4786 | | return fg.load(ptr, elem_ty, llvm_ptr_align, access_kind); |
| 4969 | return fg.load(ptr, ptr_align, elem_ty, access_kind); |
| 4787 | 4970 | } |
| 4788 | 4971 | |
| 4789 | | const containing_int_ty = try o.builder.intType(@intCast(ptr_info.packed_offset.host_size * 8)); |
| 4790 | | const containing_int = |
| 4791 | | try fg.wip.load(access_kind, containing_int_ty, ptr, llvm_ptr_align, ""); |
| 4972 | assert(!isByRef(elem_ty, zcu)); // all packable types are by-val |
| 4792 | 4973 | |
| 4793 | | const elem_bits = ptr_ty.childType(zcu).bitSize(zcu); |
| 4794 | | const shift_amt = try o.builder.intValue(containing_int_ty, ptr_info.packed_offset.bit_offset); |
| 4795 | | const shifted_value = try fg.wip.bin(.lshr, containing_int, shift_amt, ""); |
| 4796 | | const elem_llvm_ty = try o.lowerType(elem_ty); |
| 4974 | // Accepted proposal https://github.com/ziglang/zig/issues/24061 will eliminate this usage of `pt`. |
| 4975 | const backing_int_ty = try fg.pt.intType(.unsigned, @intCast(ptr_info.packed_offset.host_size * 8)); |
| 4976 | const llvm_backing_int_ty = try o.lowerType(backing_int_ty); |
| 4797 | 4977 | |
| 4798 | | if (isByRef(elem_ty, zcu)) { |
| 4799 | | const result_align = elem_ty.abiAlignment(zcu).toLlvm(); |
| 4800 | | const result_ptr = try fg.buildAlloca(elem_llvm_ty, result_align); |
| 4978 | const backing_int_val = try fg.load(ptr, ptr_align, backing_int_ty, .normal); |
| 4801 | 4979 | |
| 4802 | | const same_size_int = try o.builder.intType(@intCast(elem_bits)); |
| 4803 | | const truncated_int = try fg.wip.cast(.trunc, shifted_value, same_size_int, ""); |
| 4804 | | _ = try fg.wip.store(.normal, truncated_int, result_ptr, result_align); |
| 4805 | | return result_ptr; |
| 4806 | | } |
| 4980 | const elem_bits = ptr_ty.childType(zcu).bitSize(zcu); |
| 4981 | const shift_amt = try o.builder.intValue(llvm_backing_int_ty, ptr_info.packed_offset.bit_offset); |
| 4982 | const shifted_value = try fg.wip.bin(.lshr, backing_int_val, shift_amt, ""); |
| 4983 | const elem_llvm_ty = try o.lowerType(elem_ty); |
| 4807 | 4984 | |
| 4808 | 4985 | if (elem_ty.zigTypeTag(zcu) == .float or elem_ty.zigTypeTag(zcu) == .vector) { |
| 4809 | 4986 | const same_size_int = try o.builder.intType(@intCast(elem_bits)); |
| ... | ... | @@ -4918,21 +5095,22 @@ fn airCmpxchg( |
| 4918 | 5095 | return self.wip.select(.normal, success_bit, zero, payload, ""); |
| 4919 | 5096 | } |
| 4920 | 5097 | |
| 4921 | | assert(isByRef(optional_ty, zcu)); |
| 5098 | assert(!isByRef(operand_ty, zcu)); // can only cmpxchg non-by-ref types |
| 5099 | assert(isByRef(optional_ty, zcu)); // all optionals are by-ref |
| 4922 | 5100 | |
| 4923 | 5101 | comptime assert(optional_layout_version == 3); |
| 4924 | 5102 | |
| 4925 | 5103 | const non_null_bit = try self.wip.not(success_bit, ""); |
| 4926 | 5104 | |
| 4927 | | const payload_align = operand_ty.abiAlignment(zcu).toLlvm(); |
| 4928 | | const alloca_inst = try self.buildAlloca(try o.lowerType(optional_ty), payload_align); |
| 5105 | const payload_align = operand_ty.abiAlignment(zcu); |
| 5106 | const alloca_inst = try self.buildAlloca(try o.lowerType(optional_ty), payload_align.toLlvm()); |
| 4929 | 5107 | |
| 4930 | 5108 | // Payload is always the first field at offset 0, so address is `alloca_inst` |
| 4931 | | _ = try self.wip.store(.normal, payload, alloca_inst, payload_align); |
| 5109 | try self.store(alloca_inst, .none, payload, operand_ty, .normal); |
| 4932 | 5110 | |
| 4933 | 5111 | // Non-null bit is after payload with no padding because it has alignment 1 |
| 4934 | 5112 | const non_null_ptr = try self.ptraddConst(alloca_inst, operand_ty.abiSize(zcu)); |
| 4935 | | _ = try self.wip.store(.normal, non_null_bit, non_null_ptr, comptime .fromByteUnits(1)); |
| 5113 | try self.store(non_null_ptr, payload_align, non_null_bit, .bool, .normal); |
| 4936 | 5114 | |
| 4937 | 5115 | return alloca_inst; |
| 4938 | 5116 | } |
| ... | ... | @@ -5073,7 +5251,17 @@ fn airAtomicStore( |
| 5073 | 5251 | |
| 5074 | 5252 | self.maybeMarkAllowZeroAccess(ptr_ty.ptrInfo(zcu)); |
| 5075 | 5253 | |
| 5076 | | try self.storeFull(ptr, ptr_ty, element, ordering); |
| 5254 | assert(!isByRef(operand_ty, zcu)); |
| 5255 | |
| 5256 | _ = try self.wip.storeAtomic( |
| 5257 | if (ptr_ty.isVolatilePtr(zcu)) .@"volatile" else .normal, |
| 5258 | element, |
| 5259 | ptr, |
| 5260 | self.sync_scope, |
| 5261 | ordering, |
| 5262 | ptr_ty.ptrAlignment(zcu).toLlvm(), |
| 5263 | ); |
| 5264 | |
| 5077 | 5265 | return .none; |
| 5078 | 5266 | } |
| 5079 | 5267 | |
| ... | ... | @@ -5084,7 +5272,7 @@ fn airMemset(self: *FuncGen, inst: Air.Inst.Index, safety: bool) Allocator.Error |
| 5084 | 5272 | const dest_slice = try self.resolveInst(bin_op.lhs); |
| 5085 | 5273 | const ptr_ty = self.typeOf(bin_op.lhs); |
| 5086 | 5274 | const elem_ty = self.typeOf(bin_op.rhs); |
| 5087 | | const dest_ptr_align = ptr_ty.ptrAlignment(zcu).toLlvm(); |
| 5275 | const dest_ptr_align = ptr_ty.ptrAlignment(zcu); |
| 5088 | 5276 | const dest_ptr = try self.sliceOrArrayPtr(dest_slice, ptr_ty); |
| 5089 | 5277 | const access_kind: Builder.MemoryAccessKind = |
| 5090 | 5278 | if (ptr_ty.isVolatilePtr(zcu)) .@"volatile" else .normal; |
| ... | ... | @@ -5110,7 +5298,7 @@ fn airMemset(self: *FuncGen, inst: Air.Inst.Index, safety: bool) Allocator.Error |
| 5110 | 5298 | const len = try self.sliceOrArrayLenInBytes(dest_slice, ptr_ty); |
| 5111 | 5299 | _ = try self.wip.callMemSet( |
| 5112 | 5300 | dest_ptr, |
| 5113 | | dest_ptr_align, |
| 5301 | dest_ptr_align.toLlvm(), |
| 5114 | 5302 | fill_byte, |
| 5115 | 5303 | len, |
| 5116 | 5304 | access_kind, |
| ... | ... | @@ -5132,7 +5320,7 @@ fn airMemset(self: *FuncGen, inst: Air.Inst.Index, safety: bool) Allocator.Error |
| 5132 | 5320 | const len = try self.sliceOrArrayLenInBytes(dest_slice, ptr_ty); |
| 5133 | 5321 | _ = try self.wip.callMemSet( |
| 5134 | 5322 | dest_ptr, |
| 5135 | | dest_ptr_align, |
| 5323 | dest_ptr_align.toLlvm(), |
| 5136 | 5324 | fill_byte, |
| 5137 | 5325 | len, |
| 5138 | 5326 | access_kind, |
| ... | ... | @@ -5152,7 +5340,7 @@ fn airMemset(self: *FuncGen, inst: Air.Inst.Index, safety: bool) Allocator.Error |
| 5152 | 5340 | |
| 5153 | 5341 | _ = try self.wip.callMemSet( |
| 5154 | 5342 | dest_ptr, |
| 5155 | | dest_ptr_align, |
| 5343 | dest_ptr_align.toLlvm(), |
| 5156 | 5344 | fill_byte, |
| 5157 | 5345 | len, |
| 5158 | 5346 | access_kind, |
| ... | ... | @@ -5182,7 +5370,6 @@ fn airMemset(self: *FuncGen, inst: Air.Inst.Index, safety: bool) Allocator.Error |
| 5182 | 5370 | const body_block = try self.wip.block(1, "InlineMemsetBody"); |
| 5183 | 5371 | const end_block = try self.wip.block(1, "InlineMemsetEnd"); |
| 5184 | 5372 | |
| 5185 | | const llvm_usize_ty = try o.lowerType(.usize); |
| 5186 | 5373 | const end_ptr = switch (ptr_ty.ptrSize(zcu)) { |
| 5187 | 5374 | .slice => try self.ptraddScaled( |
| 5188 | 5375 | dest_ptr, |
| ... | ... | @@ -5201,18 +5388,8 @@ fn airMemset(self: *FuncGen, inst: Air.Inst.Index, safety: bool) Allocator.Error |
| 5201 | 5388 | |
| 5202 | 5389 | self.wip.cursor = .{ .block = body_block }; |
| 5203 | 5390 | const elem_abi_align = elem_ty.abiAlignment(zcu); |
| 5204 | | const it_ptr_align = InternPool.Alignment.fromLlvm(dest_ptr_align).min(elem_abi_align).toLlvm(); |
| 5205 | | if (isByRef(elem_ty, zcu)) { |
| 5206 | | _ = try self.wip.callMemCpy( |
| 5207 | | it_ptr.toValue(), |
| 5208 | | it_ptr_align, |
| 5209 | | value, |
| 5210 | | elem_abi_align.toLlvm(), |
| 5211 | | try o.builder.intValue(llvm_usize_ty, elem_abi_size), |
| 5212 | | access_kind, |
| 5213 | | self.disable_intrinsics, |
| 5214 | | ); |
| 5215 | | } else _ = try self.wip.store(access_kind, value, it_ptr.toValue(), it_ptr_align); |
| 5391 | const it_ptr_align: InternPool.Alignment = dest_ptr_align.min(elem_abi_align); |
| 5392 | try self.store(it_ptr.toValue(), it_ptr_align, value, elem_ty, access_kind); |
| 5216 | 5393 | const next_ptr = try self.ptraddConst(it_ptr.toValue(), elem_abi_size); |
| 5217 | 5394 | _ = try self.wip.br(loop_block); |
| 5218 | 5395 | |
| ... | ... | @@ -5289,14 +5466,11 @@ fn airSetUnionTag(self: *FuncGen, inst: Air.Inst.Index) Allocator.Error!Builder. |
| 5289 | 5466 | |
| 5290 | 5467 | const union_ptr = try self.resolveInst(bin_op.lhs); |
| 5291 | 5468 | const new_tag = try self.resolveInst(bin_op.rhs); |
| 5469 | const tag_ty = self.typeOf(bin_op.rhs); |
| 5292 | 5470 | const union_ptr_align = un_ptr_ty.ptrAlignment(zcu); |
| 5293 | | if (layout.payload_size == 0) { |
| 5294 | | _ = try self.wip.store(access_kind, new_tag, union_ptr, union_ptr_align.toLlvm()); |
| 5295 | | return .none; |
| 5296 | | } |
| 5297 | 5471 | const tag_field_ptr = try self.ptraddConst(union_ptr, layout.tagOffset()); |
| 5298 | 5472 | const tag_ptr_align = union_ptr_align.offset(layout.tagOffset()); |
| 5299 | | _ = try self.wip.store(access_kind, new_tag, tag_field_ptr, tag_ptr_align.toLlvm()); |
| 5473 | try self.store(tag_field_ptr, tag_ptr_align, new_tag, tag_ty, access_kind); |
| 5300 | 5474 | return .none; |
| 5301 | 5475 | } |
| 5302 | 5476 | |
| ... | ... | @@ -5309,9 +5483,8 @@ fn airGetUnionTag(self: *FuncGen, inst: Air.Inst.Index) Allocator.Error!Builder. |
| 5309 | 5483 | assert(layout.tag_size != 0); |
| 5310 | 5484 | const operand = try self.resolveInst(ty_op.operand); |
| 5311 | 5485 | if (isByRef(un_ty, zcu)) { |
| 5312 | | const llvm_tag_ty = try o.lowerType(un_ty.unionTagTypeRuntime(zcu).?); |
| 5313 | 5486 | const tag_field_ptr = try self.ptraddConst(operand, layout.tagOffset()); |
| 5314 | | return self.wip.load(.normal, llvm_tag_ty, tag_field_ptr, .default, ""); |
| 5487 | return self.load(tag_field_ptr, .none, un_ty.unionTagTypeRuntime(zcu).?, .normal); |
| 5315 | 5488 | } else { |
| 5316 | 5489 | // This is only possible if all fields are zero-bit, in which case `operand` is already an |
| 5317 | 5490 | // integer value (the union is lowered as its enum tag). |
| ... | ... | @@ -5480,14 +5653,13 @@ fn airErrorName(self: *FuncGen, inst: Air.Inst.Index) Allocator.Error!Builder.Va |
| 5480 | 5653 | const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; |
| 5481 | 5654 | const operand = try self.resolveInst(un_op); |
| 5482 | 5655 | const slice_ty = self.typeOfIndex(inst); |
| 5483 | | const slice_llvm_ty = try o.lowerType(slice_ty); |
| 5484 | 5656 | |
| 5485 | 5657 | // If operand is small (e.g. `u8`), then signedness becomes a problem -- GEP always treats the index as signed. |
| 5486 | 5658 | const operand_usize = try self.wip.conv(.unsigned, operand, try o.lowerType(.usize), ""); |
| 5487 | 5659 | |
| 5488 | 5660 | const error_name_table_ptr = try o.getErrorNameTable(); |
| 5489 | 5661 | const error_name_ptr = try self.ptraddScaled(error_name_table_ptr.toValue(&o.builder), operand_usize, slice_ty.abiSize(zcu)); |
| 5490 | | return self.wip.load(.normal, slice_llvm_ty, error_name_ptr, .default, ""); |
| 5662 | return self.load(error_name_ptr, .none, slice_ty, .normal); |
| 5491 | 5663 | } |
| 5492 | 5664 | |
| 5493 | 5665 | fn airSplat(self: *FuncGen, inst: Air.Inst.Index) Allocator.Error!Builder.Value { |
| ... | ... | @@ -5696,15 +5868,13 @@ fn airShuffleTwo(fg: *FuncGen, inst: Air.Inst.Index) Allocator.Error!Builder.Val |
| 5696 | 5868 | /// Reduce a vector by repeatedly applying `llvm_fn` to produce an accumulated result. |
| 5697 | 5869 | /// |
| 5698 | 5870 | /// Equivalent to: |
| 5699 | | /// reduce: { |
| 5700 | | /// var i: usize = 0; |
| 5701 | | /// var accum: T = init; |
| 5702 | | /// while (i < vec.len) : (i += 1) { |
| 5703 | | /// accum = llvm_fn(accum, vec[i]); |
| 5704 | | /// } |
| 5705 | | /// break :reduce accum; |
| 5706 | | /// } |
| 5707 | | /// |
| 5871 | /// ``` |
| 5872 | /// var accum: T = init; |
| 5873 | /// for (0..i) |i| { |
| 5874 | /// accum = llvm_fn(accum, vec[i]); |
| 5875 | /// } |
| 5876 | /// // result is 'accum' |
| 5877 | /// ``` |
| 5708 | 5878 | fn buildReducedCall( |
| 5709 | 5879 | self: *FuncGen, |
| 5710 | 5880 | llvm_fn: Builder.Function.Index, |
| ... | ... | @@ -5713,56 +5883,54 @@ fn buildReducedCall( |
| 5713 | 5883 | accum_init: Builder.Value, |
| 5714 | 5884 | ) Allocator.Error!Builder.Value { |
| 5715 | 5885 | const o = self.object; |
| 5716 | | const usize_ty = try o.lowerType(.usize); |
| 5717 | | const llvm_vector_len = try o.builder.intValue(usize_ty, vector_len); |
| 5886 | const llvm_usize_ty = try o.lowerType(.usize); |
| 5887 | const llvm_vector_len = try o.builder.intValue(llvm_usize_ty, vector_len); |
| 5718 | 5888 | const llvm_result_ty = accum_init.typeOfWip(&self.wip); |
| 5719 | 5889 | |
| 5720 | | // Allocate and initialize our mutable variables |
| 5721 | | const i_ptr = try self.buildAlloca(usize_ty, .default); |
| 5722 | | _ = try self.wip.store(.normal, try o.builder.intValue(usize_ty, 0), i_ptr, .default); |
| 5723 | | const accum_ptr = try self.buildAlloca(llvm_result_ty, .default); |
| 5724 | | _ = try self.wip.store(.normal, accum_init, accum_ptr, .default); |
| 5725 | | |
| 5726 | | // Setup the loop |
| 5727 | | const loop = try self.wip.block(2, "ReduceLoop"); |
| 5728 | | const loop_exit = try self.wip.block(1, "AfterReduce"); |
| 5729 | | _ = try self.wip.br(loop); |
| 5730 | | { |
| 5731 | | self.wip.cursor = .{ .block = loop }; |
| 5732 | | |
| 5733 | | // while (i < vec.len) |
| 5734 | | const i = try self.wip.load(.normal, usize_ty, i_ptr, .default, ""); |
| 5735 | | const cond = try self.wip.icmp(.ult, i, llvm_vector_len, ""); |
| 5736 | | const loop_then = try self.wip.block(1, "ReduceLoopThen"); |
| 5737 | | |
| 5738 | | _ = try self.wip.brCond(cond, loop_then, loop_exit, .none); |
| 5739 | | |
| 5740 | | { |
| 5741 | | self.wip.cursor = .{ .block = loop_then }; |
| 5890 | const entry_block = self.wip.cursor.block; |
| 5742 | 5891 | |
| 5743 | | // accum = f(accum, vec[i]); |
| 5744 | | const accum = try self.wip.load(.normal, llvm_result_ty, accum_ptr, .default, ""); |
| 5745 | | const element = try self.wip.extractElement(operand_vector, i, ""); |
| 5746 | | const new_accum = try self.wip.call( |
| 5747 | | .normal, |
| 5748 | | .ccc, |
| 5749 | | .none, |
| 5750 | | llvm_fn.typeOf(&o.builder), |
| 5751 | | llvm_fn.toValue(&o.builder), |
| 5752 | | &.{ accum, element }, |
| 5753 | | "", |
| 5754 | | ); |
| 5755 | | _ = try self.wip.store(.normal, new_accum, accum_ptr, .default); |
| 5892 | const cond_block = try self.wip.block(2, "ReduceLoopCond"); |
| 5893 | const body_block = try self.wip.block(1, "ReduceLoopBody"); |
| 5894 | const exit_block = try self.wip.block(1, "ReduceLoopExit"); |
| 5895 | |
| 5896 | _ = try self.wip.br(cond_block); |
| 5897 | |
| 5898 | // ReduceLoopCond: |
| 5899 | // %index = phi iN [0, %Entry], [%new_index, %ReduceLoopBody] |
| 5900 | // %accum = phi T [%accum_init, %Entry], [%new_accum, %ReduceLoopBody] |
| 5901 | // %cond = icmp ult iN %index, %vector_len |
| 5902 | // br i1 %cond, label %ReduceLoopBody, label %ReduceLoopExit |
| 5903 | self.wip.cursor = .{ .block = cond_block }; |
| 5904 | const index = try self.wip.phi(llvm_usize_ty, ""); |
| 5905 | const accum = try self.wip.phi(llvm_result_ty, ""); |
| 5906 | const cond = try self.wip.icmp(.ult, index.toValue(), llvm_vector_len, ""); |
| 5907 | _ = try self.wip.brCond(cond, body_block, exit_block, .none); |
| 5908 | |
| 5909 | // ReduceLoopBody: |
| 5910 | // %elem = extractelement <n x T> %operand_vec, iN %index |
| 5911 | // %new_accum = call T @llvm_fn(T %accum, T %elem) |
| 5912 | // %new_index = add nuw iN %index, 1 |
| 5913 | // br label %ReduceLoopCond |
| 5914 | self.wip.cursor = .{ .block = body_block }; |
| 5915 | const elem = try self.wip.extractElement(operand_vector, index.toValue(), ""); |
| 5916 | const new_accum = try self.wip.call( |
| 5917 | .normal, |
| 5918 | .ccc, |
| 5919 | .none, |
| 5920 | llvm_fn.typeOf(&o.builder), |
| 5921 | llvm_fn.toValue(&o.builder), |
| 5922 | &.{ accum.toValue(), elem }, |
| 5923 | "", |
| 5924 | ); |
| 5925 | const new_index = try self.wip.bin(.@"add nuw", index.toValue(), try o.builder.intValue(llvm_usize_ty, 1), ""); |
| 5926 | _ = try self.wip.br(cond_block); |
| 5756 | 5927 | |
| 5757 | | // i += 1 |
| 5758 | | const new_i = try self.wip.bin(.add, i, try o.builder.intValue(usize_ty, 1), ""); |
| 5759 | | _ = try self.wip.store(.normal, new_i, i_ptr, .default); |
| 5760 | | _ = try self.wip.br(loop); |
| 5761 | | } |
| 5762 | | } |
| 5928 | const index_init = try o.builder.intValue(llvm_usize_ty, 0); |
| 5929 | index.finish(&.{ index_init, new_index }, &.{ entry_block, body_block }, &self.wip); |
| 5930 | accum.finish(&.{ accum_init, new_accum }, &.{ entry_block, body_block }, &self.wip); |
| 5763 | 5931 | |
| 5764 | | self.wip.cursor = .{ .block = loop_exit }; |
| 5765 | | return self.wip.load(.normal, llvm_result_ty, accum_ptr, .default, ""); |
| 5932 | self.wip.cursor = .{ .block = exit_block }; |
| 5933 | return new_accum; |
| 5766 | 5934 | } |
| 5767 | 5935 | |
| 5768 | 5936 | fn airReduce(self: *FuncGen, inst: Air.Inst.Index, fast: Builder.FastMathKind) Allocator.Error!Builder.Value { |
| ... | ... | @@ -5939,24 +6107,7 @@ fn airAggregateInit(self: *FuncGen, inst: Air.Inst.Index) Allocator.Error!Builde |
| 5939 | 6107 | |
| 5940 | 6108 | const llvm_field_val = try self.resolveInst(elem); |
| 5941 | 6109 | |
| 5942 | | if (isByRef(field_ty, zcu)) { |
| 5943 | | _ = try self.wip.callMemCpy( |
| 5944 | | field_ptr, |
| 5945 | | field_ptr_align.toLlvm(), |
| 5946 | | llvm_field_val, |
| 5947 | | field_ty.abiAlignment(zcu).toLlvm(), |
| 5948 | | try o.builder.intValue(try o.lowerType(.usize), field_ty.abiSize(zcu)), |
| 5949 | | .normal, |
| 5950 | | self.disable_intrinsics, |
| 5951 | | ); |
| 5952 | | } else { |
| 5953 | | _ = try self.wip.store( |
| 5954 | | .normal, |
| 5955 | | llvm_field_val, |
| 5956 | | field_ptr, |
| 5957 | | field_ptr_align.toLlvm(), |
| 5958 | | ); |
| 5959 | | } |
| 6110 | try self.store(field_ptr, field_ptr_align, llvm_field_val, field_ty, .normal); |
| 5960 | 6111 | } |
| 5961 | 6112 | |
| 5962 | 6113 | return alloca_inst; |
| ... | ... | @@ -5975,12 +6126,12 @@ fn airAggregateInit(self: *FuncGen, inst: Air.Inst.Index) Allocator.Error!Builde |
| 5975 | 6126 | for (elements, 0..) |elem, i| { |
| 5976 | 6127 | const elem_ptr = try self.ptraddConst(alloca_inst, elem_size * i); |
| 5977 | 6128 | const llvm_elem = try self.resolveInst(elem); |
| 5978 | | try self.store(elem_ptr, .none, llvm_elem, array_info.elem_type); |
| 6129 | try self.store(elem_ptr, .none, llvm_elem, array_info.elem_type, .normal); |
| 5979 | 6130 | } |
| 5980 | 6131 | if (array_info.sentinel) |sent_val| { |
| 5981 | 6132 | const elem_ptr = try self.ptraddConst(alloca_inst, elem_size * array_info.len); |
| 5982 | 6133 | const llvm_elem = try self.resolveValue(sent_val); |
| 5983 | | try self.store(elem_ptr, .none, llvm_elem.toValue(), array_info.elem_type); |
| 6134 | try self.store(elem_ptr, .none, llvm_elem.toValue(), array_info.elem_type, .normal); |
| 5984 | 6135 | } |
| 5985 | 6136 | |
| 5986 | 6137 | return alloca_inst; |
| ... | ... | @@ -6014,11 +6165,12 @@ fn airUnionInit(self: *FuncGen, inst: Air.Inst.Index) Allocator.Error!Builder.Va |
| 6014 | 6165 | |
| 6015 | 6166 | { |
| 6016 | 6167 | const payload_ptr = try self.ptraddConst(result_ptr, layout.payloadOffset()); |
| 6017 | | try self.store(payload_ptr, layout.payload_align, llvm_payload, field_ty); |
| 6168 | try self.store(payload_ptr, layout.payload_align, llvm_payload, field_ty, .normal); |
| 6018 | 6169 | } |
| 6019 | 6170 | |
| 6020 | 6171 | if (layout.tag_size != 0) { |
| 6021 | | const loaded_enum = ip.loadEnumType(union_obj.enum_tag_type); |
| 6172 | const tag_ty: Type = .fromInterned(union_obj.enum_tag_type); |
| 6173 | const loaded_enum = ip.loadEnumType(tag_ty.toIntern()); |
| 6022 | 6174 | const llvm_tag_val = switch (loaded_enum.field_values.getOrNone(ip, extra.field_index)) { |
| 6023 | 6175 | .none => try o.builder.intConst( |
| 6024 | 6176 | try o.lowerType(.fromInterned(union_obj.enum_tag_type)), |
| ... | ... | @@ -6027,7 +6179,7 @@ fn airUnionInit(self: *FuncGen, inst: Air.Inst.Index) Allocator.Error!Builder.Va |
| 6027 | 6179 | else => |tag_val_ip| try o.lowerValue(tag_val_ip), |
| 6028 | 6180 | }; |
| 6029 | 6181 | const tag_ptr = try self.ptraddConst(result_ptr, layout.tagOffset()); |
| 6030 | | _ = try self.wip.store(.normal, llvm_tag_val.toValue(), tag_ptr, layout.tag_align.toLlvm()); |
| 6182 | try self.store(tag_ptr, layout.tag_align, llvm_tag_val.toValue(), tag_ty, .normal); |
| 6031 | 6183 | } |
| 6032 | 6184 | |
| 6033 | 6185 | return result_ptr; |
| ... | ... | @@ -6134,7 +6286,7 @@ fn airWorkGroupSize(self: *FuncGen, inst: Air.Inst.Index) Allocator.Error!Builde |
| 6134 | 6286 | // Load the work_group_* member from the struct as u16. |
| 6135 | 6287 | // Just treat the dispatch pointer as an array of u16 to keep things simple. |
| 6136 | 6288 | const workgroup_size_ptr = try self.ptraddConst(dispatch_ptr, (2 + dimension) * 2); |
| 6137 | | return self.wip.load(.normal, .i16, workgroup_size_ptr, comptime .fromByteUnits(2), ""); |
| 6289 | return self.load(workgroup_size_ptr, .@"2", .u16, .normal); |
| 6138 | 6290 | }, |
| 6139 | 6291 | .nvptx, .nvptx64 => { |
| 6140 | 6292 | return self.workIntrinsic(dimension, 1, "nvvm.read.ptx.sreg.ntid"); |
| ... | ... | @@ -6169,8 +6321,8 @@ fn optCmpNull( |
| 6169 | 6321 | comptime assert(optional_layout_version == 3); |
| 6170 | 6322 | // Non-null bit is always after the payload, with no padding because it has alignment 1. |
| 6171 | 6323 | const non_null_ptr = try self.ptraddConst(opt_ptr, opt_ty.optionalChild(zcu).abiSize(zcu)); |
| 6172 | | const non_null = try self.wip.load(access_kind, .i8, non_null_ptr, .default, ""); |
| 6173 | | return self.wip.icmp(cond, non_null, try self.object.builder.intValue(.i8, 0), ""); |
| 6324 | const non_null = try self.load(non_null_ptr, .@"1", .bool, access_kind); |
| 6325 | return self.wip.icmp(cond, non_null, .false, ""); |
| 6174 | 6326 | } |
| 6175 | 6327 | |
| 6176 | 6328 | /// Assumes that `Type.optionalReprIsPayload` is `false` for `opt_ty` and that the payload has bits. |
| ... | ... | @@ -6187,13 +6339,9 @@ fn optPayloadHandle( |
| 6187 | 6339 | // Payload is first field so always at the same address as the optional itself. |
| 6188 | 6340 | const payload_ptr = opt_ptr; |
| 6189 | 6341 | |
| 6190 | | const payload_align = payload_ty.abiAlignment(zcu).toLlvm(); |
| 6191 | | if (isByRef(payload_ty, zcu)) { |
| 6192 | | if (can_elide_load) return payload_ptr; |
| 6193 | | return fg.loadByRef(payload_ptr, payload_ty, payload_align, .normal); |
| 6194 | | } else { |
| 6195 | | return fg.loadTruncate(.normal, payload_ty, payload_ptr, payload_align); |
| 6196 | | } |
| 6342 | if (can_elide_load and isByRef(payload_ty, zcu)) return payload_ptr; |
| 6343 | |
| 6344 | return fg.load(payload_ptr, .none, payload_ty, .normal); |
| 6197 | 6345 | } |
| 6198 | 6346 | |
| 6199 | 6347 | fn fieldPtr( |
| ... | ... | @@ -6217,214 +6365,145 @@ fn fieldPtr( |
| 6217 | 6365 | return self.ptraddConst(aggregate_ptr, offset); |
| 6218 | 6366 | } |
| 6219 | 6367 | |
| 6220 | | /// Load a value and, if needed, mask out padding bits for non byte-sized integer values. |
| 6221 | | fn loadTruncate( |
| 6222 | | fg: *FuncGen, |
| 6223 | | access_kind: Builder.MemoryAccessKind, |
| 6224 | | payload_ty: Type, |
| 6225 | | payload_ptr: Builder.Value, |
| 6226 | | payload_alignment: Builder.Alignment, |
| 6227 | | ) Allocator.Error!Builder.Value { |
| 6228 | | // from https://llvm.org/docs/LangRef.html#load-instruction : |
| 6229 | | // "When loading a value of a type like i20 with a size that is not an integral number of bytes, the result is undefined if the value was not originally written using a store of the same type. " |
| 6230 | | // => so load the byte aligned value and trunc the unwanted bits. |
| 6231 | | |
| 6232 | | const o = fg.object; |
| 6233 | | const zcu = o.zcu; |
| 6234 | | const payload_llvm_ty = try o.lowerType(payload_ty); |
| 6235 | | const abi_size = payload_ty.abiSize(zcu); |
| 6236 | | |
| 6237 | | const load_llvm_ty = if (payload_ty.isAbiInt(zcu)) |
| 6238 | | try o.builder.intType(@intCast(abi_size * 8)) |
| 6239 | | else |
| 6240 | | payload_llvm_ty; |
| 6241 | | const loaded = try fg.wip.load(access_kind, load_llvm_ty, payload_ptr, payload_alignment, ""); |
| 6242 | | const shifted = if (payload_llvm_ty != load_llvm_ty and zcu.getTarget().cpu.arch.endian() == .big) |
| 6243 | | try fg.wip.bin(.lshr, loaded, try o.builder.intValue( |
| 6244 | | load_llvm_ty, |
| 6245 | | (payload_ty.abiSize(zcu) - (std.math.divCeil(u64, payload_ty.bitSize(zcu), 8) catch unreachable)) * 8, |
| 6246 | | ), "") |
| 6247 | | else |
| 6248 | | loaded; |
| 6249 | | |
| 6250 | | return fg.wip.conv(.unneeded, shifted, payload_llvm_ty, ""); |
| 6251 | | } |
| 6252 | | |
| 6253 | | /// Load a by-ref type by constructing a new alloca and performing a memcpy. |
| 6254 | | fn loadByRef( |
| 6255 | | fg: *FuncGen, |
| 6256 | | ptr: Builder.Value, |
| 6257 | | pointee_type: Type, |
| 6258 | | ptr_alignment: Builder.Alignment, |
| 6259 | | access_kind: Builder.MemoryAccessKind, |
| 6260 | | ) Allocator.Error!Builder.Value { |
| 6261 | | const o = fg.object; |
| 6262 | | const pointee_llvm_ty = try o.lowerType(pointee_type); |
| 6263 | | const result_align = InternPool.Alignment.fromLlvm(ptr_alignment) |
| 6264 | | .max(pointee_type.abiAlignment(o.zcu)).toLlvm(); |
| 6265 | | const result_ptr = try fg.buildAlloca(pointee_llvm_ty, result_align); |
| 6266 | | const size_bytes = pointee_type.abiSize(o.zcu); |
| 6267 | | _ = try fg.wip.callMemCpy( |
| 6268 | | result_ptr, |
| 6269 | | result_align, |
| 6270 | | ptr, |
| 6271 | | ptr_alignment, |
| 6272 | | try o.builder.intValue(try o.lowerType(.usize), size_bytes), |
| 6273 | | access_kind, |
| 6274 | | fg.disable_intrinsics, |
| 6275 | | ); |
| 6276 | | return result_ptr; |
| 6277 | | } |
| 6278 | | |
| 6279 | | /// If `isByRef` returns `true` for `elem_ty`, this still performs a copy by memcpy'ing the value |
| 6280 | | /// into a new alloca. |
| 6368 | /// Non-atomic, non-bitpacked load of type `load_ty` from pointer `ptr`. |
| 6369 | /// |
| 6370 | /// `ptr` has alignment `ptr_align`, or `load_ty.abiAlignment(zcu)` if `ptr_align` is `.none`. |
| 6371 | /// |
| 6372 | /// If `load_ty` is a by-ref type, then the value is copied to a new alloca with a memcpy, and a |
| 6373 | /// pointer to that alloca is returned. |
| 6281 | 6374 | fn load( |
| 6282 | 6375 | fg: *FuncGen, |
| 6283 | 6376 | ptr: Builder.Value, |
| 6284 | | elem_ty: Type, |
| 6285 | | ptr_alignment: Builder.Alignment, |
| 6377 | ptr_align: InternPool.Alignment, |
| 6378 | load_ty: Type, |
| 6286 | 6379 | access_kind: Builder.MemoryAccessKind, |
| 6287 | 6380 | ) Allocator.Error!Builder.Value { |
| 6288 | | const zcu = fg.object.zcu; |
| 6289 | | if (isByRef(elem_ty, zcu)) { |
| 6290 | | return fg.loadByRef(ptr, elem_ty, ptr_alignment, access_kind); |
| 6291 | | } else { |
| 6292 | | return fg.loadTruncate(access_kind, elem_ty, ptr, ptr_alignment); |
| 6293 | | } |
| 6294 | | } |
| 6295 | | |
| 6296 | | fn storeFull( |
| 6297 | | self: *FuncGen, |
| 6298 | | ptr: Builder.Value, |
| 6299 | | ptr_ty: Type, |
| 6300 | | elem: Builder.Value, |
| 6301 | | ordering: Builder.AtomicOrdering, |
| 6302 | | ) Allocator.Error!void { |
| 6303 | | const o = self.object; |
| 6381 | const o = fg.object; |
| 6304 | 6382 | const zcu = o.zcu; |
| 6305 | | const info = ptr_ty.ptrInfo(zcu); |
| 6306 | | const elem_ty = Type.fromInterned(info.child); |
| 6307 | | if (!elem_ty.hasRuntimeBits(zcu)) { |
| 6308 | | return; |
| 6309 | | } |
| 6310 | | const ptr_alignment = ptr_ty.ptrAlignment(zcu).toLlvm(); |
| 6311 | | const access_kind: Builder.MemoryAccessKind = |
| 6312 | | if (info.flags.is_volatile) .@"volatile" else .normal; |
| 6313 | | |
| 6314 | | if (info.flags.vector_index != .none) { |
| 6315 | | const index_u32 = try o.builder.intValue(.i32, info.flags.vector_index); |
| 6316 | | const vec_elem_ty = try o.lowerType(elem_ty); |
| 6317 | | const vec_ty = try o.builder.vectorType(.normal, info.packed_offset.host_size, vec_elem_ty); |
| 6318 | 6383 | |
| 6319 | | const loaded_vector = try self.wip.load(.normal, vec_ty, ptr, ptr_alignment, ""); |
| 6384 | const abi_align = load_ty.abiAlignment(zcu); |
| 6385 | const abi_size = load_ty.abiSize(zcu); |
| 6320 | 6386 | |
| 6321 | | const modified_vector = try self.wip.insertElement(loaded_vector, elem, index_u32, ""); |
| 6322 | | |
| 6323 | | assert(ordering == .none); |
| 6324 | | _ = try self.wip.store(access_kind, modified_vector, ptr, ptr_alignment); |
| 6325 | | return; |
| 6326 | | } |
| 6327 | | |
| 6328 | | if (info.packed_offset.host_size != 0) { |
| 6329 | | const containing_int_ty = try o.builder.intType(@intCast(info.packed_offset.host_size * 8)); |
| 6330 | | assert(ordering == .none); |
| 6331 | | const containing_int = |
| 6332 | | try self.wip.load(.normal, containing_int_ty, ptr, ptr_alignment, ""); |
| 6333 | | const elem_bits = ptr_ty.childType(zcu).bitSize(zcu); |
| 6334 | | const shift_amt = try o.builder.intConst(containing_int_ty, info.packed_offset.bit_offset); |
| 6335 | | // Convert to equally-sized integer type in order to perform the bit |
| 6336 | | // operations on the value to store |
| 6337 | | const value_bits_type = try o.builder.intType(@intCast(elem_bits)); |
| 6338 | | const value_bits = if (elem_ty.isPtrAtRuntime(zcu)) |
| 6339 | | try self.wip.cast(.ptrtoint, elem, value_bits_type, "") |
| 6340 | | else |
| 6341 | | try self.wip.cast(.bitcast, elem, value_bits_type, ""); |
| 6342 | | |
| 6343 | | const mask_val = blk: { |
| 6344 | | const zext = try self.wip.cast( |
| 6345 | | .zext, |
| 6346 | | try o.builder.intValue(value_bits_type, -1), |
| 6347 | | containing_int_ty, |
| 6348 | | "", |
| 6349 | | ); |
| 6350 | | const shl = try self.wip.bin(.shl, zext, shift_amt.toValue(), ""); |
| 6351 | | break :blk try self.wip.bin( |
| 6352 | | .xor, |
| 6353 | | shl, |
| 6354 | | try o.builder.intValue(containing_int_ty, -1), |
| 6355 | | "", |
| 6356 | | ); |
| 6357 | | }; |
| 6358 | | |
| 6359 | | const anded_containing_int = try self.wip.bin(.@"and", containing_int, mask_val, ""); |
| 6360 | | const extended_value = try self.wip.cast(.zext, value_bits, containing_int_ty, ""); |
| 6361 | | const shifted_value = try self.wip.bin(.shl, extended_value, shift_amt.toValue(), ""); |
| 6362 | | const ored_value = try self.wip.bin(.@"or", shifted_value, anded_containing_int, ""); |
| 6387 | const llvm_load_ty = try o.lowerType(load_ty); |
| 6388 | const llvm_ptr_align: Builder.Alignment = switch (ptr_align) { |
| 6389 | .none => abi_align.toLlvm(), |
| 6390 | else => |a| a.toLlvm(), |
| 6391 | }; |
| 6363 | 6392 | |
| 6364 | | assert(ordering == .none); |
| 6365 | | _ = try self.wip.store(access_kind, ored_value, ptr, ptr_alignment); |
| 6366 | | return; |
| 6367 | | } |
| 6368 | | if (!isByRef(elem_ty, zcu)) { |
| 6369 | | _ = try self.wip.storeAtomic( |
| 6370 | | access_kind, |
| 6371 | | elem, |
| 6393 | if (isByRef(load_ty, zcu)) { |
| 6394 | const llvm_usize_ty = try o.lowerType(.usize); |
| 6395 | const result_ptr = try fg.buildAlloca(llvm_load_ty, abi_align.toLlvm()); |
| 6396 | _ = try fg.wip.callMemCpy( |
| 6397 | result_ptr, |
| 6398 | abi_align.toLlvm(), |
| 6372 | 6399 | ptr, |
| 6373 | | self.sync_scope, |
| 6374 | | ordering, |
| 6375 | | ptr_alignment, |
| 6400 | llvm_ptr_align, |
| 6401 | try o.builder.intValue(llvm_usize_ty, abi_size), |
| 6402 | access_kind, |
| 6403 | fg.disable_intrinsics, |
| 6376 | 6404 | ); |
| 6377 | | return; |
| 6405 | return result_ptr; |
| 6378 | 6406 | } |
| 6379 | | assert(ordering == .none); |
| 6380 | | _ = try self.wip.callMemCpy( |
| 6381 | | ptr, |
| 6382 | | ptr_alignment, |
| 6383 | | elem, |
| 6384 | | elem_ty.abiAlignment(zcu).toLlvm(), |
| 6385 | | try o.builder.intValue(try o.lowerType(.usize), elem_ty.abiSize(zcu)), |
| 6386 | | access_kind, |
| 6387 | | self.disable_intrinsics, |
| 6388 | | ); |
| 6407 | |
| 6408 | if (load_ty.isAbiInt(zcu) and load_ty.bitSize(zcu) != abi_size * 8) { |
| 6409 | // `load_ty` is an integer type with padding bits. In theory, we shouldn't need any special |
| 6410 | // handling for these, as LLVM's documented semantics are a valid implementation of Zig's |
| 6411 | // semantics. However: |
| 6412 | // |
| 6413 | // * LLVM's lowering for these integer types generally leads to poor codegen, as integers |
| 6414 | // are only extended to the next byte, instead of to the next "natural" integer type. |
| 6415 | // |
| 6416 | // * Clang never emits loads or stores of these types, so LLVM's support for them is rather |
| 6417 | // flaky---we have encountered several LLVM bugs caused by incorrect handling of them. |
| 6418 | // |
| 6419 | // Therefore, we handle these memory accesses specially: in this case we will actually load |
| 6420 | // the next-largest "natural" integer type and then truncate to `load_ty`. |
| 6421 | const llvm_abi_ty = try o.builder.intType(@intCast(abi_size * 8)); |
| 6422 | const loaded = try fg.wip.load(access_kind, llvm_abi_ty, ptr, llvm_ptr_align, ""); |
| 6423 | // For packed structs, current Zig semantics don't really allow us to make the padding bits |
| 6424 | // well-defined. This should be solved once https://github.com/ziglang/zig/issues/24061 is |
| 6425 | // implemented, but until then, do a normal trunc for packed types. |
| 6426 | return fg.wip.cast(switch (load_ty.zigTypeTag(zcu)) { |
| 6427 | .@"struct", .@"union" => .trunc, |
| 6428 | else => switch (load_ty.intInfo(zcu).signedness) { |
| 6429 | .unsigned => .@"trunc nuw", |
| 6430 | .signed => .@"trunc nsw", |
| 6431 | }, |
| 6432 | }, loaded, llvm_load_ty, ""); |
| 6433 | } |
| 6434 | |
| 6435 | // `load_ty` is a simple by-val type which requires no special handling. |
| 6436 | return fg.wip.load(access_kind, llvm_load_ty, ptr, llvm_ptr_align, ""); |
| 6389 | 6437 | } |
| 6390 | 6438 | |
| 6391 | | /// Non-atomic, non-volatile, non-packed store. |
| 6439 | /// Non-atomic, non-bitpacked store of `elem` to pointer `ptr`. |
| 6440 | /// |
| 6441 | /// `ptr` has alignment `ptr_align`, or `elem_ty.abiAlignment(zcu)` if `ptr_align` is `.none`. |
| 6442 | /// |
| 6443 | /// If `elem_ty` is a by-ref type, then `elem` is itself a pointer, and a memcpy is emitted. |
| 6392 | 6444 | fn store( |
| 6393 | 6445 | fg: *FuncGen, |
| 6394 | 6446 | ptr: Builder.Value, |
| 6395 | 6447 | ptr_align: InternPool.Alignment, |
| 6396 | 6448 | elem: Builder.Value, |
| 6397 | 6449 | elem_ty: Type, |
| 6450 | access_kind: Builder.MemoryAccessKind, |
| 6398 | 6451 | ) Allocator.Error!void { |
| 6399 | 6452 | const o = fg.object; |
| 6400 | 6453 | const zcu = o.zcu; |
| 6454 | |
| 6455 | const abi_align = elem_ty.abiAlignment(zcu); |
| 6456 | const abi_size = elem_ty.abiSize(zcu); |
| 6457 | |
| 6401 | 6458 | const llvm_ptr_align = switch (ptr_align) { |
| 6402 | | .none => elem_ty.abiAlignment(zcu).toLlvm(), |
| 6459 | .none => abi_align.toLlvm(), |
| 6403 | 6460 | else => ptr_align.toLlvm(), |
| 6404 | 6461 | }; |
| 6462 | |
| 6405 | 6463 | if (isByRef(elem_ty, zcu)) { |
| 6464 | const llvm_usize_ty = try o.lowerType(.usize); |
| 6406 | 6465 | _ = try fg.wip.callMemCpy( |
| 6407 | 6466 | ptr, |
| 6408 | 6467 | llvm_ptr_align, |
| 6409 | 6468 | elem, |
| 6410 | | elem_ty.abiAlignment(zcu).toLlvm(), |
| 6411 | | try o.builder.intValue( |
| 6412 | | try o.lowerType(.usize), |
| 6413 | | elem_ty.abiSize(zcu), |
| 6414 | | ), |
| 6415 | | .normal, |
| 6469 | abi_align.toLlvm(), |
| 6470 | try o.builder.intValue(llvm_usize_ty, abi_size), |
| 6471 | access_kind, |
| 6416 | 6472 | fg.disable_intrinsics, |
| 6417 | 6473 | ); |
| 6418 | | } else { |
| 6474 | return; |
| 6475 | } |
| 6476 | |
| 6477 | assert(elem.typeOfWip(&fg.wip) == try o.lowerType(elem_ty)); |
| 6478 | |
| 6479 | if (elem_ty.isAbiInt(zcu) and elem_ty.bitSize(zcu) != abi_size * 8) { |
| 6480 | // `elem_ty` is an integer type with padding bits, so we need to handle it specially---see |
| 6481 | // the corresponding comment in `FuncGen.load` for more details. |
| 6482 | const llvm_abi_ty = try o.builder.intType(@intCast(abi_size * 8)); |
| 6483 | const extended = try fg.wip.cast(switch (elem_ty.intInfo(zcu).signedness) { |
| 6484 | .unsigned => .zext, |
| 6485 | .signed => .sext, |
| 6486 | }, elem, llvm_abi_ty, ""); |
| 6419 | 6487 | _ = try fg.wip.storeAtomic( |
| 6420 | | .normal, |
| 6421 | | elem, |
| 6488 | access_kind, |
| 6489 | extended, |
| 6422 | 6490 | ptr, |
| 6423 | 6491 | fg.sync_scope, |
| 6424 | 6492 | .none, |
| 6425 | 6493 | llvm_ptr_align, |
| 6426 | 6494 | ); |
| 6495 | return; |
| 6427 | 6496 | } |
| 6497 | |
| 6498 | // `elem_ty` is a simple by-val type which requires no special handling. |
| 6499 | _ = try fg.wip.storeAtomic( |
| 6500 | access_kind, |
| 6501 | elem, |
| 6502 | ptr, |
| 6503 | fg.sync_scope, |
| 6504 | .none, |
| 6505 | llvm_ptr_align, |
| 6506 | ); |
| 6428 | 6507 | } |
| 6429 | 6508 | |
| 6430 | 6509 | fn valgrindMarkUndef(fg: *FuncGen, ptr: Builder.Value, len: Builder.Value) Allocator.Error!void { |
| ... | ... | @@ -6453,18 +6532,18 @@ fn valgrindClientRequest( |
| 6453 | 6532 | if (!target_util.hasValgrindSupport(target, .stage2_llvm)) return default_value; |
| 6454 | 6533 | |
| 6455 | 6534 | const llvm_usize = try o.lowerType(.usize); |
| 6456 | | const usize_alignment = Type.usize.abiAlignment(zcu).toLlvm(); |
| 6535 | const usize_align = Type.usize.abiAlignment(zcu).toLlvm(); |
| 6457 | 6536 | |
| 6458 | 6537 | const array_llvm_ty = try o.builder.arrayType(6, llvm_usize); |
| 6459 | 6538 | const array_ptr = if (fg.valgrind_client_request_array == .none) a: { |
| 6460 | | const array_ptr = try fg.buildAlloca(array_llvm_ty, usize_alignment); |
| 6539 | const array_ptr = try fg.buildAlloca(array_llvm_ty, usize_align); |
| 6461 | 6540 | fg.valgrind_client_request_array = array_ptr; |
| 6462 | 6541 | break :a array_ptr; |
| 6463 | 6542 | } else fg.valgrind_client_request_array; |
| 6464 | 6543 | const array_elements = [_]Builder.Value{ request, a1, a2, a3, a4, a5 }; |
| 6465 | 6544 | for (array_elements, 0..) |elem, i| { |
| 6466 | 6545 | const elem_ptr = try fg.ptraddConst(array_ptr, i * Type.usize.abiSize(zcu)); |
| 6467 | | _ = try fg.wip.store(.normal, elem, elem_ptr, usize_alignment); |
| 6546 | try fg.store(elem_ptr, .none, elem, .usize, .normal); |
| 6468 | 6547 | } |
| 6469 | 6548 | |
| 6470 | 6549 | const arch_specific: struct { |
| ... | ... | @@ -7283,33 +7362,6 @@ fn isScalar(zcu: *Zcu, ty: Type) bool { |
| 7283 | 7362 | }; |
| 7284 | 7363 | } |
| 7285 | 7364 | |
| 7286 | | pub fn buildAllocaInner( |
| 7287 | | wip: *Builder.WipFunction, |
| 7288 | | llvm_ty: Builder.Type, |
| 7289 | | alignment: Builder.Alignment, |
| 7290 | | target: *const std.Target, |
| 7291 | | ) Allocator.Error!Builder.Value { |
| 7292 | | const address_space = llvmAllocaAddressSpace(target); |
| 7293 | | |
| 7294 | | const alloca = blk: { |
| 7295 | | const prev_cursor = wip.cursor; |
| 7296 | | const prev_debug_location = wip.debug_location; |
| 7297 | | defer { |
| 7298 | | wip.cursor = prev_cursor; |
| 7299 | | if (wip.cursor.block == .entry) wip.cursor.instruction += 1; |
| 7300 | | wip.debug_location = prev_debug_location; |
| 7301 | | } |
| 7302 | | |
| 7303 | | wip.cursor = .{ .block = .entry }; |
| 7304 | | wip.debug_location = .no_location; |
| 7305 | | break :blk try wip.alloca(.normal, llvm_ty, .none, alignment, address_space, ""); |
| 7306 | | }; |
| 7307 | | |
| 7308 | | // The pointer returned from this function should have the generic address space, |
| 7309 | | // if this isn't the case then cast it to the generic address space. |
| 7310 | | return wip.conv(.unneeded, alloca, .ptr, ""); |
| 7311 | | } |
| 7312 | | |
| 7313 | 7365 | /// This is the one source of truth for whether a type is passed around as an LLVM pointer, |
| 7314 | 7366 | /// or as an LLVM value. |
| 7315 | 7367 | pub fn isByRef(ty: Type, zcu: *const Zcu) bool { |
| ... | ... | @@ -7380,7 +7432,11 @@ fn getAtomicAbiType(fg: *const FuncGen, ty: Type, is_rmw_xchg: bool) Allocator.E |
| 7380 | 7432 | } |
| 7381 | 7433 | |
| 7382 | 7434 | fn ptraddConst(fg: *FuncGen, ptr: Builder.Value, offset: u64) Allocator.Error!Builder.Value { |
| 7383 | | return fg.object.ptraddConst(&fg.wip, ptr, offset); |
| 7435 | if (offset == 0) return ptr; |
| 7436 | const o = fg.object; |
| 7437 | const llvm_usize_ty = try o.lowerType(.usize); |
| 7438 | const offset_val = try o.builder.intValue(llvm_usize_ty, offset); |
| 7439 | return fg.wip.gep(.inbounds, .i8, ptr, &.{offset_val}, ""); |
| 7384 | 7440 | } |
| 7385 | 7441 | fn ptraddScaled(fg: *FuncGen, ptr: Builder.Value, index: Builder.Value, scale: u64) Allocator.Error!Builder.Value { |
| 7386 | 7442 | if (scale == 0) return ptr; |