| ... | ... | @@ -4539,7 +4539,7 @@ pub const FuncGen = struct { |
| 4539 | 4539 | } |
| 4540 | 4540 | // We need to emit instructions to check for equality/inequality |
| 4541 | 4541 | // of optionals that are not pointers. |
| 4542 | | const is_by_ref = isByRef(operand_ty); |
| 4542 | const is_by_ref = isByRef(scalar_ty); |
| 4543 | 4543 | const lhs_non_null = self.optIsNonNull(lhs, is_by_ref); |
| 4544 | 4544 | const rhs_non_null = self.optIsNonNull(rhs, is_by_ref); |
| 4545 | 4545 | const llvm_i2 = self.context.intType(2); |
| ... | ... | @@ -4564,8 +4564,8 @@ pub const FuncGen = struct { |
| 4564 | 4564 | _ = self.builder.buildBr(end_block); |
| 4565 | 4565 | |
| 4566 | 4566 | self.builder.positionBuilderAtEnd(both_pl_block); |
| 4567 | | const lhs_payload = self.optPayloadHandle(lhs, is_by_ref); |
| 4568 | | const rhs_payload = self.optPayloadHandle(rhs, is_by_ref); |
| 4567 | const lhs_payload = self.optPayloadHandle(lhs, scalar_ty); |
| 4568 | const rhs_payload = self.optPayloadHandle(rhs, scalar_ty); |
| 4569 | 4569 | const payload_cmp = try self.cmp(lhs_payload, rhs_payload, payload_ty, op); |
| 4570 | 4570 | _ = self.builder.buildBr(end_block); |
| 4571 | 4571 | const both_pl_block_end = self.builder.getInsertBlock(); |
| ... | ... | @@ -5795,7 +5795,7 @@ pub const FuncGen = struct { |
| 5795 | 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 | 5801 | fn airErrUnionPayload( |
| ... | ... | @@ -7347,11 +7347,9 @@ pub const FuncGen = struct { |
| 7347 | 7347 | } |
| 7348 | 7348 | |
| 7349 | 7349 | comptime assert(optional_layout_version == 3); |
| 7350 | | const optional_llvm_ty = try self.dg.lowerType(optional_ty); |
| 7350 | |
| 7351 | 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), ""); |
| 7353 | | const partial = self.builder.buildInsertValue(optional_llvm_ty.getUndef(), payload, 0, ""); |
| 7354 | | return self.builder.buildInsertValue(partial, non_null_field, 1, ""); |
| 7352 | return buildOptional(self, optional_ty, payload, non_null_bit); |
| 7355 | 7353 | } |
| 7356 | 7354 | |
| 7357 | 7355 | fn airAtomicRmw(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { |
| ... | ... | @@ -7515,11 +7513,13 @@ pub const FuncGen = struct { |
| 7515 | 7513 | const union_ptr = try self.resolveInst(bin_op.lhs); |
| 7516 | 7514 | const new_tag = try self.resolveInst(bin_op.rhs); |
| 7517 | 7515 | if (layout.payload_size == 0) { |
| 7516 | // TODO alignment on this store |
| 7518 | 7517 | _ = self.builder.buildStore(new_tag, union_ptr); |
| 7519 | 7518 | return null; |
| 7520 | 7519 | } |
| 7521 | 7520 | const tag_index = @boolToInt(layout.tag_align < layout.payload_align); |
| 7522 | 7521 | const tag_field_ptr = self.builder.buildStructGEP(union_ptr, tag_index, ""); |
| 7522 | // TODO alignment on this store |
| 7523 | 7523 | _ = self.builder.buildStore(new_tag, tag_field_ptr); |
| 7524 | 7524 | return null; |
| 7525 | 7525 | } |
| ... | ... | @@ -8355,18 +8355,77 @@ pub const FuncGen = struct { |
| 8355 | 8355 | } |
| 8356 | 8356 | |
| 8357 | 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 { |
| 8359 | | if (is_by_ref) { |
| 8358 | fn optPayloadHandle( |
| 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 | 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 | 8369 | const indices: [2]*const llvm.Value = .{ |
| 8363 | 8370 | index_type.constNull(), // dereference the pointer |
| 8364 | 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 | 8431 | fn fieldPtr( |
| ... | ... | @@ -9335,7 +9394,18 @@ fn isByRef(ty: Type) bool { |
| 9335 | 9394 | .ErrorUnion => return isByRef(ty.errorUnionPayload()), |
| 9336 | 9395 | .Optional => { |
| 9337 | 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 | } |