| ... | @@ -4539,7 +4539,7 @@ pub const FuncGen = struct { | ... | @@ -4539,7 +4539,7 @@ pub const FuncGen = struct { |
| 4539 | } | 4539 | } |
| 4540 | // We need to emit instructions to check for equality/inequality | 4540 | // We need to emit instructions to check for equality/inequality |
| 4541 | // of optionals that are not pointers. | 4541 | // of optionals that are not pointers. |
| 4542 | const is_by_ref = isByRef(operand_ty); | 4542 | const is_by_ref = isByRef(scalar_ty); |
| 4543 | const lhs_non_null = self.optIsNonNull(lhs, is_by_ref); | 4543 | const lhs_non_null = self.optIsNonNull(lhs, is_by_ref); |
| 4544 | const rhs_non_null = self.optIsNonNull(rhs, is_by_ref); | 4544 | const rhs_non_null = self.optIsNonNull(rhs, is_by_ref); |
| 4545 | const llvm_i2 = self.context.intType(2); | 4545 | const llvm_i2 = self.context.intType(2); |
| ... | @@ -4564,8 +4564,8 @@ pub const FuncGen = struct { | ... | @@ -4564,8 +4564,8 @@ pub const FuncGen = struct { |
| 4564 | _ = self.builder.buildBr(end_block); | 4564 | _ = self.builder.buildBr(end_block); |
| 4565 | | 4565 | |
| 4566 | self.builder.positionBuilderAtEnd(both_pl_block); | 4566 | self.builder.positionBuilderAtEnd(both_pl_block); |
| 4567 | const lhs_payload = self.optPayloadHandle(lhs, is_by_ref); | 4567 | const lhs_payload = self.optPayloadHandle(lhs, scalar_ty); |
| 4568 | const rhs_payload = self.optPayloadHandle(rhs, is_by_ref); | 4568 | const rhs_payload = self.optPayloadHandle(rhs, scalar_ty); |
| 4569 | const payload_cmp = try self.cmp(lhs_payload, rhs_payload, payload_ty, op); | 4569 | const payload_cmp = try self.cmp(lhs_payload, rhs_payload, payload_ty, op); |
| 4570 | _ = self.builder.buildBr(end_block); | 4570 | _ = self.builder.buildBr(end_block); |
| 4571 | const both_pl_block_end = self.builder.getInsertBlock(); | 4571 | const both_pl_block_end = self.builder.getInsertBlock(); |
| ... | @@ -5795,7 +5795,7 @@ pub const FuncGen = struct { | ... | @@ -5795,7 +5795,7 @@ pub const FuncGen = struct { |
| 5795 | return operand; | 5795 | return operand; |
| 5796 | } | 5796 | } |
| 5797 | | 5797 | |
| 5798 | return self.optPayloadHandle(operand, isByRef(payload_ty)); | 5798 | return self.optPayloadHandle(operand, optional_ty); |
| 5799 | } | 5799 | } |
| 5800 | | 5800 | |
| 5801 | fn airErrUnionPayload( | 5801 | fn airErrUnionPayload( |
| ... | @@ -7347,11 +7347,9 @@ pub const FuncGen = struct { | ... | @@ -7347,11 +7347,9 @@ pub const FuncGen = struct { |
| 7347 | } | 7347 | } |
| 7348 | | 7348 | |
| 7349 | comptime assert(optional_layout_version == 3); | 7349 | comptime assert(optional_layout_version == 3); |
| 7350 | const optional_llvm_ty = try self.dg.lowerType(optional_ty); | 7350 | |
| 7351 | const non_null_bit = self.builder.buildNot(success_bit, ""); | 7351 | const non_null_bit = self.builder.buildNot(success_bit, ""); |
| 7352 | const non_null_field = self.builder.buildZExt(non_null_bit, self.dg.context.intType(8), ""); | 7352 | return buildOptional(self, optional_ty, payload, non_null_bit); |
| 7353 | const partial = self.builder.buildInsertValue(optional_llvm_ty.getUndef(), payload, 0, ""); | | |
| 7354 | return self.builder.buildInsertValue(partial, non_null_field, 1, ""); | | |
| 7355 | } | 7353 | } |
| 7356 | | 7354 | |
| 7357 | fn airAtomicRmw(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { | 7355 | fn airAtomicRmw(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { |
| ... | @@ -7515,11 +7513,13 @@ pub const FuncGen = struct { | ... | @@ -7515,11 +7513,13 @@ pub const FuncGen = struct { |
| 7515 | const union_ptr = try self.resolveInst(bin_op.lhs); | 7513 | const union_ptr = try self.resolveInst(bin_op.lhs); |
| 7516 | const new_tag = try self.resolveInst(bin_op.rhs); | 7514 | const new_tag = try self.resolveInst(bin_op.rhs); |
| 7517 | if (layout.payload_size == 0) { | 7515 | if (layout.payload_size == 0) { |
| | 7516 | // TODO alignment on this store |
| 7518 | _ = self.builder.buildStore(new_tag, union_ptr); | 7517 | _ = self.builder.buildStore(new_tag, union_ptr); |
| 7519 | return null; | 7518 | return null; |
| 7520 | } | 7519 | } |
| 7521 | const tag_index = @boolToInt(layout.tag_align < layout.payload_align); | 7520 | const tag_index = @boolToInt(layout.tag_align < layout.payload_align); |
| 7522 | const tag_field_ptr = self.builder.buildStructGEP(union_ptr, tag_index, ""); | 7521 | const tag_field_ptr = self.builder.buildStructGEP(union_ptr, tag_index, ""); |
| | 7522 | // TODO alignment on this store |
| 7523 | _ = self.builder.buildStore(new_tag, tag_field_ptr); | 7523 | _ = self.builder.buildStore(new_tag, tag_field_ptr); |
| 7524 | return null; | 7524 | return null; |
| 7525 | } | 7525 | } |
| ... | @@ -8355,18 +8355,77 @@ pub const FuncGen = struct { | ... | @@ -8355,18 +8355,77 @@ pub const FuncGen = struct { |
| 8355 | } | 8355 | } |
| 8356 | | 8356 | |
| 8357 | /// Assumes the optional is not pointer-like and payload has bits. | 8357 | /// Assumes the optional is not pointer-like and payload has bits. |
| 8358 | fn optPayloadHandle(self: *FuncGen, opt_handle: *const llvm.Value, is_by_ref: bool) *const llvm.Value { | 8358 | fn optPayloadHandle( |
| 8359 | if (is_by_ref) { | 8359 | fg: *FuncGen, |
| | 8360 | opt_handle: *const llvm.Value, |
| | 8361 | opt_ty: Type, |
| | 8362 | ) *const llvm.Value { |
| | 8363 | var buf: Type.Payload.ElemType = undefined; |
| | 8364 | const payload_ty = opt_ty.optionalChild(&buf); |
| | 8365 | |
| | 8366 | if (isByRef(opt_ty)) { |
| 8360 | // We have a pointer and we need to return a pointer to the first field. | 8367 | // We have a pointer and we need to return a pointer to the first field. |
| 8361 | const index_type = self.context.intType(32); | 8368 | const index_type = fg.context.intType(32); |
| 8362 | const indices: [2]*const llvm.Value = .{ | 8369 | const indices: [2]*const llvm.Value = .{ |
| 8363 | index_type.constNull(), // dereference the pointer | 8370 | index_type.constNull(), // dereference the pointer |
| 8364 | index_type.constNull(), // first field is the payload | 8371 | index_type.constNull(), // first field is the payload |
| 8365 | }; | 8372 | }; |
| 8366 | return self.builder.buildInBoundsGEP(opt_handle, &indices, indices.len, ""); | 8373 | const payload_ptr = fg.builder.buildInBoundsGEP(opt_handle, &indices, indices.len, ""); |
| | 8374 | |
| | 8375 | if (isByRef(payload_ty)) { |
| | 8376 | return payload_ptr; |
| | 8377 | } |
| | 8378 | const target = fg.dg.module.getTarget(); |
| | 8379 | const payload_alignment = payload_ty.abiAlignment(target); |
| | 8380 | const load_inst = fg.builder.buildLoad(payload_ptr, ""); |
| | 8381 | load_inst.setAlignment(payload_alignment); |
| | 8382 | return load_inst; |
| | 8383 | } |
| | 8384 | |
| | 8385 | assert(!isByRef(payload_ty)); |
| | 8386 | return fg.builder.buildExtractValue(opt_handle, 0, ""); |
| | 8387 | } |
| | 8388 | |
| | 8389 | fn buildOptional( |
| | 8390 | self: *FuncGen, |
| | 8391 | optional_ty: Type, |
| | 8392 | payload: *const llvm.Value, |
| | 8393 | non_null_bit: *const llvm.Value, |
| | 8394 | ) !?*const llvm.Value { |
| | 8395 | const optional_llvm_ty = try self.dg.lowerType(optional_ty); |
| | 8396 | const non_null_field = self.builder.buildZExt(non_null_bit, self.dg.context.intType(8), ""); |
| | 8397 | |
| | 8398 | if (isByRef(optional_ty)) { |
| | 8399 | const target = self.dg.module.getTarget(); |
| | 8400 | const alloca_inst = self.buildAlloca(optional_llvm_ty); |
| | 8401 | const payload_alignment = optional_ty.abiAlignment(target); |
| | 8402 | alloca_inst.setAlignment(payload_alignment); |
| | 8403 | |
| | 8404 | const index_type = self.context.intType(32); |
| | 8405 | { |
| | 8406 | const indices: [2]*const llvm.Value = .{ |
| | 8407 | index_type.constNull(), // dereference the pointer |
| | 8408 | index_type.constNull(), // first field is the payload |
| | 8409 | }; |
| | 8410 | const field_ptr = self.builder.buildInBoundsGEP(alloca_inst, &indices, indices.len, ""); |
| | 8411 | const store_inst = self.builder.buildStore(payload, field_ptr); |
| | 8412 | store_inst.setAlignment(payload_alignment); |
| | 8413 | } |
| | 8414 | { |
| | 8415 | const indices: [2]*const llvm.Value = .{ |
| | 8416 | index_type.constNull(), // dereference the pointer |
| | 8417 | index_type.constInt(1, .False), // second field is the non-null bit |
| | 8418 | }; |
| | 8419 | const field_ptr = self.builder.buildInBoundsGEP(alloca_inst, &indices, indices.len, ""); |
| | 8420 | const store_inst = self.builder.buildStore(non_null_field, field_ptr); |
| | 8421 | store_inst.setAlignment(1); |
| | 8422 | } |
| | 8423 | |
| | 8424 | return alloca_inst; |
| 8367 | } | 8425 | } |
| 8368 | | 8426 | |
| 8369 | return self.builder.buildExtractValue(opt_handle, 0, ""); | 8427 | const partial = self.builder.buildInsertValue(optional_llvm_ty.getUndef(), payload, 0, ""); |
| | 8428 | return self.builder.buildInsertValue(partial, non_null_field, 1, ""); |
| 8370 | } | 8429 | } |
| 8371 | | 8430 | |
| 8372 | fn fieldPtr( | 8431 | fn fieldPtr( |
| ... | @@ -9335,7 +9394,18 @@ fn isByRef(ty: Type) bool { | ... | @@ -9335,7 +9394,18 @@ fn isByRef(ty: Type) bool { |
| 9335 | .ErrorUnion => return isByRef(ty.errorUnionPayload()), | 9394 | .ErrorUnion => return isByRef(ty.errorUnionPayload()), |
| 9336 | .Optional => { | 9395 | .Optional => { |
| 9337 | var buf: Type.Payload.ElemType = undefined; | 9396 | var buf: Type.Payload.ElemType = undefined; |
| 9338 | return isByRef(ty.optionalChild(&buf)); | 9397 | const payload_ty = ty.optionalChild(&buf); |
| | 9398 | if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { |
| | 9399 | return false; |
| | 9400 | } |
| | 9401 | if (ty.optionalReprIsPayload()) { |
| | 9402 | return false; |
| | 9403 | } |
| | 9404 | return true; |
| | 9405 | // TODO we actually want this logic: |
| | 9406 | // however it is tripping an LLVM 14 regression: |
| | 9407 | // https://github.com/llvm/llvm-project/issues/56585 |
| | 9408 | //return isByRef(payload_ty); |
| 9339 | }, | 9409 | }, |
| 9340 | } | 9410 | } |
| 9341 | } | 9411 | } |