| ... | ... | @@ -369,17 +369,6 @@ pub const DeclGen = struct { |
| 369 | 369 | .composite_integer, |
| 370 | 370 | }; |
| 371 | 371 | }, |
| 372 | | .Enum => blk: { |
| 373 | | var buffer: Type.Payload.Bits = undefined; |
| 374 | | const int_ty = ty.intTagType(&buffer); |
| 375 | | const int_info = int_ty.intInfo(target); |
| 376 | | break :blk ArithmeticTypeInfo{ |
| 377 | | .bits = int_info.bits, |
| 378 | | .is_vector = false, |
| 379 | | .signedness = int_info.signedness, |
| 380 | | .class = .integer, |
| 381 | | }; |
| 382 | | }, |
| 383 | 372 | // As of yet, there is no vector support in the self-hosted compiler. |
| 384 | 373 | .Vector => self.todo("implement arithmeticTypeInfo for Vector", .{}), |
| 385 | 374 | // TODO: For which types is this the case? |
| ... | ... | @@ -1742,12 +1731,12 @@ pub const DeclGen = struct { |
| 1742 | 1731 | .struct_field_ptr_index_2 => try self.airStructFieldPtrIndex(inst, 2), |
| 1743 | 1732 | .struct_field_ptr_index_3 => try self.airStructFieldPtrIndex(inst, 3), |
| 1744 | 1733 | |
| 1745 | | .cmp_eq => try self.airCmp(inst, .OpFOrdEqual, .OpLogicalEqual, .OpIEqual), |
| 1746 | | .cmp_neq => try self.airCmp(inst, .OpFOrdNotEqual, .OpLogicalNotEqual, .OpINotEqual), |
| 1747 | | .cmp_gt => try self.airCmp(inst, .OpFOrdGreaterThan, .OpSGreaterThan, .OpUGreaterThan), |
| 1748 | | .cmp_gte => try self.airCmp(inst, .OpFOrdGreaterThanEqual, .OpSGreaterThanEqual, .OpUGreaterThanEqual), |
| 1749 | | .cmp_lt => try self.airCmp(inst, .OpFOrdLessThan, .OpSLessThan, .OpULessThan), |
| 1750 | | .cmp_lte => try self.airCmp(inst, .OpFOrdLessThanEqual, .OpSLessThanEqual, .OpULessThanEqual), |
| 1734 | .cmp_eq => try self.airCmp(inst, .eq), |
| 1735 | .cmp_neq => try self.airCmp(inst, .neq), |
| 1736 | .cmp_gt => try self.airCmp(inst, .gt), |
| 1737 | .cmp_gte => try self.airCmp(inst, .gte), |
| 1738 | .cmp_lt => try self.airCmp(inst, .lt), |
| 1739 | .cmp_lte => try self.airCmp(inst, .lte), |
| 1751 | 1740 | |
| 1752 | 1741 | .arg => self.airArg(), |
| 1753 | 1742 | .alloc => try self.airAlloc(inst), |
| ... | ... | @@ -2039,58 +2028,122 @@ pub const DeclGen = struct { |
| 2039 | 2028 | return result_id; |
| 2040 | 2029 | } |
| 2041 | 2030 | |
| 2042 | | fn airCmp(self: *DeclGen, inst: Air.Inst.Index, comptime fop: Opcode, comptime sop: Opcode, comptime uop: Opcode) !?IdRef { |
| 2043 | | if (self.liveness.isUnused(inst)) return null; |
| 2044 | | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 2045 | | var lhs_id = try self.resolve(bin_op.lhs); |
| 2046 | | var rhs_id = try self.resolve(bin_op.rhs); |
| 2047 | | const result_id = self.spv.allocId(); |
| 2048 | | const result_type_id = try self.resolveTypeId(Type.bool); |
| 2049 | | const op_ty = self.air.typeOf(bin_op.lhs); |
| 2050 | | assert(op_ty.eql(self.air.typeOf(bin_op.rhs), self.module)); |
| 2031 | fn cmp( |
| 2032 | self: *DeclGen, |
| 2033 | comptime op: std.math.CompareOperator, |
| 2034 | bool_ty_id: IdRef, |
| 2035 | ty: Type, |
| 2036 | lhs_id: IdRef, |
| 2037 | rhs_id: IdRef, |
| 2038 | ) !IdRef { |
| 2039 | var cmp_lhs_id = lhs_id; |
| 2040 | var cmp_rhs_id = rhs_id; |
| 2041 | const opcode: Opcode = opcode: { |
| 2042 | var int_buffer: Type.Payload.Bits = undefined; |
| 2043 | const op_ty = switch (ty.zigTypeTag()) { |
| 2044 | .Int, .Bool, .Float => ty, |
| 2045 | .Enum => ty.intTagType(&int_buffer), |
| 2046 | .ErrorSet => Type.u16, |
| 2047 | .Pointer => blk: { |
| 2048 | // Note that while SPIR-V offers OpPtrEqual and OpPtrNotEqual, they are |
| 2049 | // currently not implemented in the SPIR-V LLVM translator. Thus, we emit these using |
| 2050 | // OpConvertPtrToU... |
| 2051 | cmp_lhs_id = self.spv.allocId(); |
| 2052 | cmp_rhs_id = self.spv.allocId(); |
| 2053 | |
| 2054 | const usize_ty_id = self.typeId(try self.sizeType()); |
| 2055 | |
| 2056 | try self.func.body.emit(self.spv.gpa, .OpConvertPtrToU, .{ |
| 2057 | .id_result_type = usize_ty_id, |
| 2058 | .id_result = cmp_lhs_id, |
| 2059 | .pointer = lhs_id, |
| 2060 | }); |
| 2051 | 2061 | |
| 2052 | | // Comparisons are generally applicable to both scalar and vector operations in SPIR-V, |
| 2053 | | // but int and float versions of operations require different opcodes. |
| 2054 | | const info = try self.arithmeticTypeInfo(op_ty); |
| 2062 | try self.func.body.emit(self.spv.gpa, .OpConvertPtrToU, .{ |
| 2063 | .id_result_type = usize_ty_id, |
| 2064 | .id_result = cmp_rhs_id, |
| 2065 | .pointer = rhs_id, |
| 2066 | }); |
| 2055 | 2067 | |
| 2056 | | const opcode_index: usize = switch (info.class) { |
| 2057 | | .composite_integer => { |
| 2058 | | return self.todo("binary operations for composite integers", .{}); |
| 2059 | | }, |
| 2060 | | .float => 0, |
| 2061 | | .bool => 1, |
| 2062 | | .strange_integer => blk: { |
| 2063 | | const op_ty_ref = try self.resolveType(op_ty, .direct); |
| 2064 | | lhs_id = try self.maskStrangeInt(op_ty_ref, lhs_id, info.bits); |
| 2065 | | rhs_id = try self.maskStrangeInt(op_ty_ref, rhs_id, info.bits); |
| 2066 | | break :blk switch (info.signedness) { |
| 2067 | | .signed => @as(usize, 1), |
| 2068 | | .unsigned => @as(usize, 2), |
| 2069 | | }; |
| 2070 | | }, |
| 2071 | | .integer => switch (info.signedness) { |
| 2072 | | .signed => @as(usize, 1), |
| 2073 | | .unsigned => @as(usize, 2), |
| 2074 | | }, |
| 2075 | | }; |
| 2068 | break :blk Type.usize; |
| 2069 | }, |
| 2070 | .Optional => unreachable, // TODO |
| 2071 | else => unreachable, |
| 2072 | }; |
| 2076 | 2073 | |
| 2077 | | const operands = .{ |
| 2078 | | .id_result_type = result_type_id, |
| 2079 | | .id_result = result_id, |
| 2080 | | .operand_1 = lhs_id, |
| 2081 | | .operand_2 = rhs_id, |
| 2082 | | }; |
| 2074 | const info = try self.arithmeticTypeInfo(op_ty); |
| 2075 | const signedness = switch (info.class) { |
| 2076 | .composite_integer => { |
| 2077 | return self.todo("binary operations for composite integers", .{}); |
| 2078 | }, |
| 2079 | .float => break :opcode switch (op) { |
| 2080 | .eq => .OpFOrdEqual, |
| 2081 | .neq => .OpFOrdNotEqual, |
| 2082 | .lt => .OpFOrdLessThan, |
| 2083 | .lte => .OpFOrdLessThanEqual, |
| 2084 | .gt => .OpFOrdGreaterThan, |
| 2085 | .gte => .OpFOrdGreaterThanEqual, |
| 2086 | }, |
| 2087 | .bool => break :opcode switch (op) { |
| 2088 | .eq => .OpIEqual, |
| 2089 | .neq => .OpINotEqual, |
| 2090 | else => unreachable, |
| 2091 | }, |
| 2092 | .strange_integer => sign: { |
| 2093 | const op_ty_ref = try self.resolveType(op_ty, .direct); |
| 2094 | // Mask operands before performing comparison. |
| 2095 | cmp_lhs_id = try self.maskStrangeInt(op_ty_ref, cmp_lhs_id, info.bits); |
| 2096 | cmp_rhs_id = try self.maskStrangeInt(op_ty_ref, cmp_rhs_id, info.bits); |
| 2097 | break :sign info.signedness; |
| 2098 | }, |
| 2099 | .integer => info.signedness, |
| 2100 | }; |
| 2083 | 2101 | |
| 2084 | | switch (opcode_index) { |
| 2085 | | 0 => try self.func.body.emit(self.spv.gpa, fop, operands), |
| 2086 | | 1 => try self.func.body.emit(self.spv.gpa, sop, operands), |
| 2087 | | 2 => try self.func.body.emit(self.spv.gpa, uop, operands), |
| 2088 | | else => unreachable, |
| 2089 | | } |
| 2102 | break :opcode switch (signedness) { |
| 2103 | .unsigned => switch (op) { |
| 2104 | .eq => .OpIEqual, |
| 2105 | .neq => .OpINotEqual, |
| 2106 | .lt => .OpULessThan, |
| 2107 | .lte => .OpULessThanEqual, |
| 2108 | .gt => .OpUGreaterThan, |
| 2109 | .gte => .OpUGreaterThanEqual, |
| 2110 | }, |
| 2111 | .signed => switch (op) { |
| 2112 | .eq => .OpIEqual, |
| 2113 | .neq => .OpINotEqual, |
| 2114 | .lt => .OpSLessThan, |
| 2115 | .lte => .OpSLessThanEqual, |
| 2116 | .gt => .OpSGreaterThan, |
| 2117 | .gte => .OpSGreaterThanEqual, |
| 2118 | }, |
| 2119 | }; |
| 2120 | }; |
| 2090 | 2121 | |
| 2122 | const result_id = self.spv.allocId(); |
| 2123 | try self.func.body.emitRaw(self.spv.gpa, opcode, 4); |
| 2124 | self.func.body.writeOperand(spec.IdResultType, bool_ty_id); |
| 2125 | self.func.body.writeOperand(spec.IdResult, result_id); |
| 2126 | self.func.body.writeOperand(spec.IdResultType, cmp_lhs_id); |
| 2127 | self.func.body.writeOperand(spec.IdResultType, cmp_rhs_id); |
| 2091 | 2128 | return result_id; |
| 2092 | 2129 | } |
| 2093 | 2130 | |
| 2131 | fn airCmp( |
| 2132 | self: *DeclGen, |
| 2133 | inst: Air.Inst.Index, |
| 2134 | comptime op: std.math.CompareOperator, |
| 2135 | ) !?IdRef { |
| 2136 | if (self.liveness.isUnused(inst)) return null; |
| 2137 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 2138 | const lhs_id = try self.resolve(bin_op.lhs); |
| 2139 | const rhs_id = try self.resolve(bin_op.rhs); |
| 2140 | const bool_ty_id = try self.resolveTypeId(Type.bool); |
| 2141 | const ty = self.air.typeOf(bin_op.lhs); |
| 2142 | assert(ty.eql(self.air.typeOf(bin_op.rhs), self.module)); |
| 2143 | |
| 2144 | return try self.cmp(op, bool_ty_id, ty, lhs_id, rhs_id); |
| 2145 | } |
| 2146 | |
| 2094 | 2147 | fn bitcast(self: *DeclGen, target_type_id: IdResultType, value_id: IdRef) !IdRef { |
| 2095 | 2148 | const result_id = self.spv.allocId(); |
| 2096 | 2149 | try self.func.body.emit(self.spv.gpa, .OpBitcast, .{ |