| ... | ... | @@ -1931,11 +1931,93 @@ pub const FuncGen = struct { |
| 1931 | 1931 | const lhs = try self.resolveInst(bin_op.lhs); |
| 1932 | 1932 | const rhs = try self.resolveInst(bin_op.rhs); |
| 1933 | 1933 | const operand_ty = self.air.typeOf(bin_op.lhs); |
| 1934 | | var buffer: Type.Payload.Bits = undefined; |
| 1934 | |
| 1935 | return self.cmp(lhs, rhs, operand_ty, op); |
| 1936 | } |
| 1937 | |
| 1938 | fn cmp( |
| 1939 | self: *FuncGen, |
| 1940 | lhs: *const llvm.Value, |
| 1941 | rhs: *const llvm.Value, |
| 1942 | operand_ty: Type, |
| 1943 | op: math.CompareOperator, |
| 1944 | ) *const llvm.Value { |
| 1945 | var int_buffer: Type.Payload.Bits = undefined; |
| 1946 | var opt_buffer: Type.Payload.ElemType = undefined; |
| 1935 | 1947 | |
| 1936 | 1948 | const int_ty = switch (operand_ty.zigTypeTag()) { |
| 1937 | | .Enum => operand_ty.intTagType(&buffer), |
| 1938 | | .Int, .Bool, .Pointer, .Optional, .ErrorSet => operand_ty, |
| 1949 | .Enum => operand_ty.intTagType(&int_buffer), |
| 1950 | .Int, .Bool, .Pointer, .ErrorSet => operand_ty, |
| 1951 | .Optional => blk: { |
| 1952 | const payload_ty = operand_ty.optionalChild(&opt_buffer); |
| 1953 | if (!payload_ty.hasCodeGenBits() or operand_ty.isPtrLikeOptional()) { |
| 1954 | break :blk operand_ty; |
| 1955 | } |
| 1956 | // We need to emit instructions to check for equality/inequality |
| 1957 | // of optionals that are not pointers. |
| 1958 | const is_by_ref = isByRef(operand_ty); |
| 1959 | const lhs_non_null = self.optIsNonNull(lhs, is_by_ref); |
| 1960 | const rhs_non_null = self.optIsNonNull(rhs, is_by_ref); |
| 1961 | const llvm_i2 = self.context.intType(2); |
| 1962 | const lhs_non_null_i2 = self.builder.buildZExt(lhs_non_null, llvm_i2, ""); |
| 1963 | const rhs_non_null_i2 = self.builder.buildZExt(rhs_non_null, llvm_i2, ""); |
| 1964 | const lhs_shifted = self.builder.buildShl(lhs_non_null_i2, llvm_i2.constInt(1, .False), ""); |
| 1965 | const lhs_rhs_ored = self.builder.buildOr(lhs_shifted, rhs_non_null_i2, ""); |
| 1966 | const both_null_block = self.context.appendBasicBlock(self.llvm_func, "BothNull"); |
| 1967 | const mixed_block = self.context.appendBasicBlock(self.llvm_func, "Mixed"); |
| 1968 | const both_pl_block = self.context.appendBasicBlock(self.llvm_func, "BothNonNull"); |
| 1969 | const end_block = self.context.appendBasicBlock(self.llvm_func, "End"); |
| 1970 | const llvm_switch = self.builder.buildSwitch(lhs_rhs_ored, mixed_block, 2); |
| 1971 | const llvm_i2_00 = llvm_i2.constInt(0b00, .False); |
| 1972 | const llvm_i2_11 = llvm_i2.constInt(0b11, .False); |
| 1973 | llvm_switch.addCase(llvm_i2_00, both_null_block); |
| 1974 | llvm_switch.addCase(llvm_i2_11, both_pl_block); |
| 1975 | |
| 1976 | self.builder.positionBuilderAtEnd(both_null_block); |
| 1977 | _ = self.builder.buildBr(end_block); |
| 1978 | |
| 1979 | self.builder.positionBuilderAtEnd(mixed_block); |
| 1980 | _ = self.builder.buildBr(end_block); |
| 1981 | |
| 1982 | self.builder.positionBuilderAtEnd(both_pl_block); |
| 1983 | const lhs_payload = self.optPayloadHandle(lhs, is_by_ref); |
| 1984 | const rhs_payload = self.optPayloadHandle(rhs, is_by_ref); |
| 1985 | const payload_cmp = self.cmp(lhs_payload, rhs_payload, payload_ty, op); |
| 1986 | _ = self.builder.buildBr(end_block); |
| 1987 | const both_pl_block_end = self.builder.getInsertBlock(); |
| 1988 | |
| 1989 | self.builder.positionBuilderAtEnd(end_block); |
| 1990 | const incoming_blocks: [3]*const llvm.BasicBlock = .{ |
| 1991 | both_null_block, |
| 1992 | mixed_block, |
| 1993 | both_pl_block_end, |
| 1994 | }; |
| 1995 | const llvm_i1 = self.context.intType(1); |
| 1996 | const llvm_i1_0 = llvm_i1.constInt(0, .False); |
| 1997 | const llvm_i1_1 = llvm_i1.constInt(1, .False); |
| 1998 | const incoming_values: [3]*const llvm.Value = .{ |
| 1999 | switch (op) { |
| 2000 | .eq => llvm_i1_1, |
| 2001 | .neq => llvm_i1_0, |
| 2002 | else => unreachable, |
| 2003 | }, |
| 2004 | switch (op) { |
| 2005 | .eq => llvm_i1_0, |
| 2006 | .neq => llvm_i1_1, |
| 2007 | else => unreachable, |
| 2008 | }, |
| 2009 | payload_cmp, |
| 2010 | }; |
| 2011 | |
| 2012 | const phi_node = self.builder.buildPhi(llvm_i1, ""); |
| 2013 | comptime assert(incoming_values.len == incoming_blocks.len); |
| 2014 | phi_node.addIncoming( |
| 2015 | &incoming_values, |
| 2016 | &incoming_blocks, |
| 2017 | incoming_values.len, |
| 2018 | ); |
| 2019 | return phi_node; |
| 2020 | }, |
| 1939 | 2021 | .Float => { |
| 1940 | 2022 | const operation: llvm.RealPredicate = switch (op) { |
| 1941 | 2023 | .eq => .OEQ, |
| ... | ... | @@ -2493,24 +2575,8 @@ pub const FuncGen = struct { |
| 2493 | 2575 | } |
| 2494 | 2576 | } |
| 2495 | 2577 | |
| 2496 | | if (operand_is_ptr or isByRef(optional_ty)) { |
| 2497 | | const index_type = self.context.intType(32); |
| 2498 | | |
| 2499 | | const indices: [2]*const llvm.Value = .{ |
| 2500 | | index_type.constNull(), |
| 2501 | | index_type.constInt(1, .False), |
| 2502 | | }; |
| 2503 | | |
| 2504 | | const field_ptr = self.builder.buildInBoundsGEP(operand, &indices, indices.len, ""); |
| 2505 | | const non_null_bit = self.builder.buildLoad(field_ptr, ""); |
| 2506 | | if (invert) { |
| 2507 | | return self.builder.buildNot(non_null_bit, ""); |
| 2508 | | } else { |
| 2509 | | return non_null_bit; |
| 2510 | | } |
| 2511 | | } |
| 2512 | | |
| 2513 | | const non_null_bit = self.builder.buildExtractValue(operand, 1, ""); |
| 2578 | const is_by_ref = operand_is_ptr or isByRef(optional_ty); |
| 2579 | const non_null_bit = self.optIsNonNull(operand, is_by_ref); |
| 2514 | 2580 | if (invert) { |
| 2515 | 2581 | return self.builder.buildNot(non_null_bit, ""); |
| 2516 | 2582 | } else { |
| ... | ... | @@ -2622,17 +2688,7 @@ pub const FuncGen = struct { |
| 2622 | 2688 | return operand; |
| 2623 | 2689 | } |
| 2624 | 2690 | |
| 2625 | | if (isByRef(payload_ty)) { |
| 2626 | | // We have a pointer and we need to return a pointer to the first field. |
| 2627 | | const index_type = self.context.intType(32); |
| 2628 | | const indices: [2]*const llvm.Value = .{ |
| 2629 | | index_type.constNull(), // dereference the pointer |
| 2630 | | index_type.constNull(), // first field is the payload |
| 2631 | | }; |
| 2632 | | return self.builder.buildInBoundsGEP(operand, &indices, indices.len, ""); |
| 2633 | | } |
| 2634 | | |
| 2635 | | return self.builder.buildExtractValue(operand, 0, ""); |
| 2691 | return self.optPayloadHandle(operand, isByRef(payload_ty)); |
| 2636 | 2692 | } |
| 2637 | 2693 | |
| 2638 | 2694 | fn airErrUnionPayload( |
| ... | ... | @@ -3748,6 +3804,38 @@ pub const FuncGen = struct { |
| 3748 | 3804 | } |
| 3749 | 3805 | } |
| 3750 | 3806 | |
| 3807 | /// Assumes the optional is not pointer-like and payload has bits. |
| 3808 | fn optIsNonNull(self: *FuncGen, opt_handle: *const llvm.Value, is_by_ref: bool) *const llvm.Value { |
| 3809 | if (is_by_ref) { |
| 3810 | const index_type = self.context.intType(32); |
| 3811 | |
| 3812 | const indices: [2]*const llvm.Value = .{ |
| 3813 | index_type.constNull(), |
| 3814 | index_type.constInt(1, .False), |
| 3815 | }; |
| 3816 | |
| 3817 | const field_ptr = self.builder.buildInBoundsGEP(opt_handle, &indices, indices.len, ""); |
| 3818 | return self.builder.buildLoad(field_ptr, ""); |
| 3819 | } |
| 3820 | |
| 3821 | return self.builder.buildExtractValue(opt_handle, 1, ""); |
| 3822 | } |
| 3823 | |
| 3824 | /// Assumes the optional is not pointer-like and payload has bits. |
| 3825 | fn optPayloadHandle(self: *FuncGen, opt_handle: *const llvm.Value, is_by_ref: bool) *const llvm.Value { |
| 3826 | if (is_by_ref) { |
| 3827 | // We have a pointer and we need to return a pointer to the first field. |
| 3828 | const index_type = self.context.intType(32); |
| 3829 | const indices: [2]*const llvm.Value = .{ |
| 3830 | index_type.constNull(), // dereference the pointer |
| 3831 | index_type.constNull(), // first field is the payload |
| 3832 | }; |
| 3833 | return self.builder.buildInBoundsGEP(opt_handle, &indices, indices.len, ""); |
| 3834 | } |
| 3835 | |
| 3836 | return self.builder.buildExtractValue(opt_handle, 0, ""); |
| 3837 | } |
| 3838 | |
| 3751 | 3839 | fn callFloor(self: *FuncGen, arg: *const llvm.Value, ty: Type) !*const llvm.Value { |
| 3752 | 3840 | return self.callFloatUnary(arg, ty, "floor"); |
| 3753 | 3841 | } |