| ... | ... | @@ -1401,7 +1401,8 @@ fn allocRegs( |
| 1401 | 1401 | for (read_args) |arg, i| { |
| 1402 | 1402 | const mcv = try arg.bind.resolveToMcv(self); |
| 1403 | 1403 | if (mcv == .register) { |
| 1404 | | arg.reg.* = mcv.register; |
| 1404 | const raw_reg = mcv.register; |
| 1405 | arg.reg.* = self.registerAlias(raw_reg, arg.ty); |
| 1405 | 1406 | } else { |
| 1406 | 1407 | const track_inst: ?Air.Inst.Index = switch (arg.bind) { |
| 1407 | 1408 | .inst => |inst| Air.refToIndex(inst).?, |
| ... | ... | @@ -1418,7 +1419,8 @@ fn allocRegs( |
| 1418 | 1419 | const operand_mapping = reuse_metadata.?.operand_mapping; |
| 1419 | 1420 | const arg = write_args[0]; |
| 1420 | 1421 | if (arg.bind == .reg) { |
| 1421 | | arg.reg.* = arg.bind.reg; |
| 1422 | const raw_reg = arg.bind.reg; |
| 1423 | arg.reg.* = self.registerAlias(raw_reg, arg.ty); |
| 1422 | 1424 | } else { |
| 1423 | 1425 | reuse_operand: for (read_args) |read_arg, i| { |
| 1424 | 1426 | if (read_arg.bind == .inst) { |
| ... | ... | @@ -1428,7 +1430,8 @@ fn allocRegs( |
| 1428 | 1430 | std.meta.eql(arg.class, read_arg.class) and |
| 1429 | 1431 | self.reuseOperand(inst, operand, operand_mapping[i], mcv)) |
| 1430 | 1432 | { |
| 1431 | | arg.reg.* = mcv.register; |
| 1433 | const raw_reg = mcv.register; |
| 1434 | arg.reg.* = self.registerAlias(raw_reg, arg.ty); |
| 1432 | 1435 | write_locks[0] = null; |
| 1433 | 1436 | reused_read_arg = i; |
| 1434 | 1437 | break :reuse_operand; |
| ... | ... | @@ -1443,7 +1446,8 @@ fn allocRegs( |
| 1443 | 1446 | } else { |
| 1444 | 1447 | for (write_args) |arg, i| { |
| 1445 | 1448 | if (arg.bind == .reg) { |
| 1446 | | arg.reg.* = arg.bind.reg; |
| 1449 | const raw_reg = arg.bind.reg; |
| 1450 | arg.reg.* = self.registerAlias(raw_reg, arg.ty); |
| 1447 | 1451 | } else { |
| 1448 | 1452 | const raw_reg = try self.register_manager.allocReg(null, arg.class); |
| 1449 | 1453 | arg.reg.* = self.registerAlias(raw_reg, arg.ty); |
| ... | ... | @@ -1887,189 +1891,6 @@ const BinOpMetadata = struct { |
| 1887 | 1891 | rhs: Air.Inst.Ref, |
| 1888 | 1892 | }; |
| 1889 | 1893 | |
| 1890 | | /// For all your binary operation needs, this function will generate |
| 1891 | | /// the corresponding Mir instruction(s). Returns the location of the |
| 1892 | | /// result. |
| 1893 | | /// |
| 1894 | | /// If the binary operation itself happens to be an Air instruction, |
| 1895 | | /// pass the corresponding index in the inst parameter. That helps |
| 1896 | | /// this function do stuff like reusing operands. |
| 1897 | | /// |
| 1898 | | /// This function does not do any lowering to Mir itself, but instead |
| 1899 | | /// looks at the lhs and rhs and determines which kind of lowering |
| 1900 | | /// would be best suitable and then delegates the lowering to other |
| 1901 | | /// functions. |
| 1902 | | fn binOp( |
| 1903 | | self: *Self, |
| 1904 | | tag: Air.Inst.Tag, |
| 1905 | | lhs: MCValue, |
| 1906 | | rhs: MCValue, |
| 1907 | | lhs_ty: Type, |
| 1908 | | rhs_ty: Type, |
| 1909 | | metadata: ?BinOpMetadata, |
| 1910 | | ) InnerError!MCValue { |
| 1911 | | const mod = self.bin_file.options.module.?; |
| 1912 | | switch (tag) { |
| 1913 | | .addwrap, |
| 1914 | | .subwrap, |
| 1915 | | .mulwrap, |
| 1916 | | => { |
| 1917 | | const base_tag: Air.Inst.Tag = switch (tag) { |
| 1918 | | .addwrap => .add, |
| 1919 | | .subwrap => .sub, |
| 1920 | | .mulwrap => .mul, |
| 1921 | | else => unreachable, |
| 1922 | | }; |
| 1923 | | |
| 1924 | | const lhs_bind = if (metadata) |md| |
| 1925 | | ReadArg.Bind{ .inst = md.lhs } |
| 1926 | | else |
| 1927 | | ReadArg.Bind{ .mcv = lhs }; |
| 1928 | | const rhs_bind = if (metadata) |md| |
| 1929 | | ReadArg.Bind{ .inst = md.rhs } |
| 1930 | | else |
| 1931 | | ReadArg.Bind{ .mcv = rhs }; |
| 1932 | | |
| 1933 | | // Generate an add/sub/mul |
| 1934 | | const maybe_inst: ?Air.Inst.Index = if (metadata) |md| md.inst else null; |
| 1935 | | const result = try self.addSub(base_tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst); |
| 1936 | | |
| 1937 | | // Truncate if necessary |
| 1938 | | switch (lhs_ty.zigTypeTag()) { |
| 1939 | | .Vector => return self.fail("TODO binary operations on vectors", .{}), |
| 1940 | | .Int => { |
| 1941 | | const int_info = lhs_ty.intInfo(self.target.*); |
| 1942 | | if (int_info.bits <= 64) { |
| 1943 | | const result_reg = result.register; |
| 1944 | | try self.truncRegister(result_reg, result_reg, int_info.signedness, int_info.bits); |
| 1945 | | return result; |
| 1946 | | } else { |
| 1947 | | return self.fail("TODO binary operations on integers > u64/i64", .{}); |
| 1948 | | } |
| 1949 | | }, |
| 1950 | | else => unreachable, |
| 1951 | | } |
| 1952 | | }, |
| 1953 | | .bit_and, |
| 1954 | | .bit_or, |
| 1955 | | .xor, |
| 1956 | | => { |
| 1957 | | switch (lhs_ty.zigTypeTag()) { |
| 1958 | | .Vector => return self.fail("TODO binary operations on vectors", .{}), |
| 1959 | | .Int => { |
| 1960 | | assert(lhs_ty.eql(rhs_ty, mod)); |
| 1961 | | const int_info = lhs_ty.intInfo(self.target.*); |
| 1962 | | if (int_info.bits <= 64) { |
| 1963 | | // TODO implement bitwise operations with immediates |
| 1964 | | const mir_tag: Mir.Inst.Tag = switch (tag) { |
| 1965 | | .bit_and => .and_shifted_register, |
| 1966 | | .bit_or => .orr_shifted_register, |
| 1967 | | .xor => .eor_shifted_register, |
| 1968 | | else => unreachable, |
| 1969 | | }; |
| 1970 | | |
| 1971 | | return try self.binOpRegister(mir_tag, lhs, rhs, lhs_ty, rhs_ty, metadata); |
| 1972 | | } else { |
| 1973 | | return self.fail("TODO binary operations on int with bits > 64", .{}); |
| 1974 | | } |
| 1975 | | }, |
| 1976 | | else => unreachable, |
| 1977 | | } |
| 1978 | | }, |
| 1979 | | .shl_exact, |
| 1980 | | .shr_exact, |
| 1981 | | => { |
| 1982 | | switch (lhs_ty.zigTypeTag()) { |
| 1983 | | .Vector => return self.fail("TODO binary operations on vectors", .{}), |
| 1984 | | .Int => { |
| 1985 | | const int_info = lhs_ty.intInfo(self.target.*); |
| 1986 | | if (int_info.bits <= 64) { |
| 1987 | | const rhs_immediate_ok = rhs == .immediate; |
| 1988 | | |
| 1989 | | const mir_tag_register: Mir.Inst.Tag = switch (tag) { |
| 1990 | | .shl_exact => .lsl_register, |
| 1991 | | .shr_exact => switch (int_info.signedness) { |
| 1992 | | .signed => Mir.Inst.Tag.asr_register, |
| 1993 | | .unsigned => Mir.Inst.Tag.lsr_register, |
| 1994 | | }, |
| 1995 | | else => unreachable, |
| 1996 | | }; |
| 1997 | | const mir_tag_immediate: Mir.Inst.Tag = switch (tag) { |
| 1998 | | .shl_exact => .lsl_immediate, |
| 1999 | | .shr_exact => switch (int_info.signedness) { |
| 2000 | | .signed => Mir.Inst.Tag.asr_immediate, |
| 2001 | | .unsigned => Mir.Inst.Tag.lsr_immediate, |
| 2002 | | }, |
| 2003 | | else => unreachable, |
| 2004 | | }; |
| 2005 | | |
| 2006 | | if (rhs_immediate_ok) { |
| 2007 | | return try self.binOpImmediate(mir_tag_immediate, lhs, rhs, lhs_ty, false, metadata); |
| 2008 | | } else { |
| 2009 | | return try self.binOpRegister(mir_tag_register, lhs, rhs, lhs_ty, rhs_ty, metadata); |
| 2010 | | } |
| 2011 | | } else { |
| 2012 | | return self.fail("TODO binary operations on int with bits > 64", .{}); |
| 2013 | | } |
| 2014 | | }, |
| 2015 | | else => unreachable, |
| 2016 | | } |
| 2017 | | }, |
| 2018 | | .shl, |
| 2019 | | .shr, |
| 2020 | | => { |
| 2021 | | const base_tag: Air.Inst.Tag = switch (tag) { |
| 2022 | | .shl => .shl_exact, |
| 2023 | | .shr => .shr_exact, |
| 2024 | | else => unreachable, |
| 2025 | | }; |
| 2026 | | |
| 2027 | | // Generate a shl_exact/shr_exact |
| 2028 | | const result = try self.binOp(base_tag, lhs, rhs, lhs_ty, rhs_ty, metadata); |
| 2029 | | |
| 2030 | | // Truncate if necessary |
| 2031 | | switch (tag) { |
| 2032 | | .shr => return result, |
| 2033 | | .shl => switch (lhs_ty.zigTypeTag()) { |
| 2034 | | .Vector => return self.fail("TODO binary operations on vectors", .{}), |
| 2035 | | .Int => { |
| 2036 | | const int_info = lhs_ty.intInfo(self.target.*); |
| 2037 | | if (int_info.bits <= 64) { |
| 2038 | | const result_reg = result.register; |
| 2039 | | try self.truncRegister(result_reg, result_reg, int_info.signedness, int_info.bits); |
| 2040 | | return result; |
| 2041 | | } else { |
| 2042 | | return self.fail("TODO binary operations on integers > u64/i64", .{}); |
| 2043 | | } |
| 2044 | | }, |
| 2045 | | else => unreachable, |
| 2046 | | }, |
| 2047 | | else => unreachable, |
| 2048 | | } |
| 2049 | | }, |
| 2050 | | .bool_and, |
| 2051 | | .bool_or, |
| 2052 | | => { |
| 2053 | | switch (lhs_ty.zigTypeTag()) { |
| 2054 | | .Bool => { |
| 2055 | | assert(lhs != .immediate); // should have been handled by Sema |
| 2056 | | assert(rhs != .immediate); // should have been handled by Sema |
| 2057 | | |
| 2058 | | const mir_tag_register: Mir.Inst.Tag = switch (tag) { |
| 2059 | | .bool_and => .and_shifted_register, |
| 2060 | | .bool_or => .orr_shifted_register, |
| 2061 | | else => unreachable, |
| 2062 | | }; |
| 2063 | | |
| 2064 | | return try self.binOpRegister(mir_tag_register, lhs, rhs, lhs_ty, rhs_ty, metadata); |
| 2065 | | }, |
| 2066 | | else => unreachable, |
| 2067 | | } |
| 2068 | | }, |
| 2069 | | else => unreachable, |
| 2070 | | } |
| 2071 | | } |
| 2072 | | |
| 2073 | 1894 | fn addSub( |
| 2074 | 1895 | self: *Self, |
| 2075 | 1896 | tag: Air.Inst.Tag, |
| ... | ... | @@ -2369,6 +2190,189 @@ fn modulo( |
| 2369 | 2190 | } |
| 2370 | 2191 | } |
| 2371 | 2192 | |
| 2193 | fn wrappingArithmetic( |
| 2194 | self: *Self, |
| 2195 | tag: Air.Inst.Tag, |
| 2196 | lhs_bind: ReadArg.Bind, |
| 2197 | rhs_bind: ReadArg.Bind, |
| 2198 | lhs_ty: Type, |
| 2199 | rhs_ty: Type, |
| 2200 | maybe_inst: ?Air.Inst.Index, |
| 2201 | ) InnerError!MCValue { |
| 2202 | switch (lhs_ty.zigTypeTag()) { |
| 2203 | .Vector => return self.fail("TODO binary operations on vectors", .{}), |
| 2204 | .Int => { |
| 2205 | const int_info = lhs_ty.intInfo(self.target.*); |
| 2206 | if (int_info.bits <= 64) { |
| 2207 | // Generate an add/sub/mul |
| 2208 | const result: MCValue = switch (tag) { |
| 2209 | .addwrap => try self.addSub(.add, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst), |
| 2210 | .subwrap => try self.addSub(.sub, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst), |
| 2211 | .mulwrap => try self.mul(lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst), |
| 2212 | else => unreachable, |
| 2213 | }; |
| 2214 | |
| 2215 | // Truncate if necessary |
| 2216 | const result_reg = result.register; |
| 2217 | try self.truncRegister(result_reg, result_reg, int_info.signedness, int_info.bits); |
| 2218 | return result; |
| 2219 | } else { |
| 2220 | return self.fail("TODO binary operations on integers > u64/i64", .{}); |
| 2221 | } |
| 2222 | }, |
| 2223 | else => unreachable, |
| 2224 | } |
| 2225 | } |
| 2226 | |
| 2227 | fn bitwise( |
| 2228 | self: *Self, |
| 2229 | tag: Air.Inst.Tag, |
| 2230 | lhs_bind: ReadArg.Bind, |
| 2231 | rhs_bind: ReadArg.Bind, |
| 2232 | lhs_ty: Type, |
| 2233 | rhs_ty: Type, |
| 2234 | maybe_inst: ?Air.Inst.Index, |
| 2235 | ) InnerError!MCValue { |
| 2236 | const mod = self.bin_file.options.module.?; |
| 2237 | switch (lhs_ty.zigTypeTag()) { |
| 2238 | .Vector => return self.fail("TODO binary operations on vectors", .{}), |
| 2239 | .Int => { |
| 2240 | assert(lhs_ty.eql(rhs_ty, mod)); |
| 2241 | const int_info = lhs_ty.intInfo(self.target.*); |
| 2242 | if (int_info.bits <= 64) { |
| 2243 | // TODO implement bitwise operations with immediates |
| 2244 | const mir_tag: Mir.Inst.Tag = switch (tag) { |
| 2245 | .bit_and => .and_shifted_register, |
| 2246 | .bit_or => .orr_shifted_register, |
| 2247 | .xor => .eor_shifted_register, |
| 2248 | else => unreachable, |
| 2249 | }; |
| 2250 | |
| 2251 | return try self.binOpRegisterNew(mir_tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst); |
| 2252 | } else { |
| 2253 | return self.fail("TODO binary operations on int with bits > 64", .{}); |
| 2254 | } |
| 2255 | }, |
| 2256 | else => unreachable, |
| 2257 | } |
| 2258 | } |
| 2259 | |
| 2260 | fn shiftExact( |
| 2261 | self: *Self, |
| 2262 | tag: Air.Inst.Tag, |
| 2263 | lhs_bind: ReadArg.Bind, |
| 2264 | rhs_bind: ReadArg.Bind, |
| 2265 | lhs_ty: Type, |
| 2266 | rhs_ty: Type, |
| 2267 | maybe_inst: ?Air.Inst.Index, |
| 2268 | ) InnerError!MCValue { |
| 2269 | _ = rhs_ty; |
| 2270 | |
| 2271 | switch (lhs_ty.zigTypeTag()) { |
| 2272 | .Vector => return self.fail("TODO binary operations on vectors", .{}), |
| 2273 | .Int => { |
| 2274 | const int_info = lhs_ty.intInfo(self.target.*); |
| 2275 | if (int_info.bits <= 64) { |
| 2276 | const rhs_immediate = try rhs_bind.resolveToImmediate(self); |
| 2277 | |
| 2278 | const mir_tag_register: Mir.Inst.Tag = switch (tag) { |
| 2279 | .shl_exact => .lsl_register, |
| 2280 | .shr_exact => switch (int_info.signedness) { |
| 2281 | .signed => Mir.Inst.Tag.asr_register, |
| 2282 | .unsigned => Mir.Inst.Tag.lsr_register, |
| 2283 | }, |
| 2284 | else => unreachable, |
| 2285 | }; |
| 2286 | const mir_tag_immediate: Mir.Inst.Tag = switch (tag) { |
| 2287 | .shl_exact => .lsl_immediate, |
| 2288 | .shr_exact => switch (int_info.signedness) { |
| 2289 | .signed => Mir.Inst.Tag.asr_immediate, |
| 2290 | .unsigned => Mir.Inst.Tag.lsr_immediate, |
| 2291 | }, |
| 2292 | else => unreachable, |
| 2293 | }; |
| 2294 | |
| 2295 | if (rhs_immediate) |imm| { |
| 2296 | return try self.binOpImmediateNew(mir_tag_immediate, lhs_bind, imm, lhs_ty, false, maybe_inst); |
| 2297 | } else { |
| 2298 | // We intentionally pass lhs_ty here in order to |
| 2299 | // prevent using the 32-bit register alias when |
| 2300 | // lhs_ty is > 32 bits. |
| 2301 | return try self.binOpRegisterNew(mir_tag_register, lhs_bind, rhs_bind, lhs_ty, lhs_ty, maybe_inst); |
| 2302 | } |
| 2303 | } else { |
| 2304 | return self.fail("TODO binary operations on int with bits > 64", .{}); |
| 2305 | } |
| 2306 | }, |
| 2307 | else => unreachable, |
| 2308 | } |
| 2309 | } |
| 2310 | |
| 2311 | fn shiftNormal( |
| 2312 | self: *Self, |
| 2313 | tag: Air.Inst.Tag, |
| 2314 | lhs_bind: ReadArg.Bind, |
| 2315 | rhs_bind: ReadArg.Bind, |
| 2316 | lhs_ty: Type, |
| 2317 | rhs_ty: Type, |
| 2318 | maybe_inst: ?Air.Inst.Index, |
| 2319 | ) InnerError!MCValue { |
| 2320 | switch (lhs_ty.zigTypeTag()) { |
| 2321 | .Vector => return self.fail("TODO binary operations on vectors", .{}), |
| 2322 | .Int => { |
| 2323 | const int_info = lhs_ty.intInfo(self.target.*); |
| 2324 | if (int_info.bits <= 64) { |
| 2325 | // Generate a shl_exact/shr_exact |
| 2326 | const result: MCValue = switch (tag) { |
| 2327 | .shl => try self.shiftExact(.shl_exact, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst), |
| 2328 | .shr => try self.shiftExact(.shr_exact, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst), |
| 2329 | else => unreachable, |
| 2330 | }; |
| 2331 | |
| 2332 | // Truncate if necessary |
| 2333 | switch (tag) { |
| 2334 | .shr => return result, |
| 2335 | .shl => { |
| 2336 | const result_reg = result.register; |
| 2337 | try self.truncRegister(result_reg, result_reg, int_info.signedness, int_info.bits); |
| 2338 | return result; |
| 2339 | }, |
| 2340 | else => unreachable, |
| 2341 | } |
| 2342 | } else { |
| 2343 | return self.fail("TODO binary operations on integers > u64/i64", .{}); |
| 2344 | } |
| 2345 | }, |
| 2346 | else => unreachable, |
| 2347 | } |
| 2348 | } |
| 2349 | |
| 2350 | fn booleanOp( |
| 2351 | self: *Self, |
| 2352 | tag: Air.Inst.Tag, |
| 2353 | lhs_bind: ReadArg.Bind, |
| 2354 | rhs_bind: ReadArg.Bind, |
| 2355 | lhs_ty: Type, |
| 2356 | rhs_ty: Type, |
| 2357 | maybe_inst: ?Air.Inst.Index, |
| 2358 | ) InnerError!MCValue { |
| 2359 | switch (lhs_ty.zigTypeTag()) { |
| 2360 | .Bool => { |
| 2361 | assert((try lhs_bind.resolveToImmediate(self)) == null); // should have been handled by Sema |
| 2362 | assert((try rhs_bind.resolveToImmediate(self)) == null); // should have been handled by Sema |
| 2363 | |
| 2364 | const mir_tag_register: Mir.Inst.Tag = switch (tag) { |
| 2365 | .bool_and => .and_shifted_register, |
| 2366 | .bool_or => .orr_shifted_register, |
| 2367 | else => unreachable, |
| 2368 | }; |
| 2369 | |
| 2370 | return try self.binOpRegisterNew(mir_tag_register, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst); |
| 2371 | }, |
| 2372 | else => unreachable, |
| 2373 | } |
| 2374 | } |
| 2375 | |
| 2372 | 2376 | fn ptrArithmetic( |
| 2373 | 2377 | self: *Self, |
| 2374 | 2378 | tag: Air.Inst.Tag, |
| ... | ... | @@ -2441,16 +2445,24 @@ fn airBinOp(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void { |
| 2441 | 2445 | |
| 2442 | 2446 | .mod => try self.modulo(lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), |
| 2443 | 2447 | |
| 2444 | | else => blk: { |
| 2445 | | const lhs = try self.resolveInst(bin_op.lhs); |
| 2446 | | const rhs = try self.resolveInst(bin_op.rhs); |
| 2448 | .addwrap => try self.wrappingArithmetic(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), |
| 2449 | .subwrap => try self.wrappingArithmetic(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), |
| 2450 | .mulwrap => try self.wrappingArithmetic(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), |
| 2447 | 2451 | |
| 2448 | | break :blk try self.binOp(tag, lhs, rhs, lhs_ty, rhs_ty, BinOpMetadata{ |
| 2449 | | .inst = inst, |
| 2450 | | .lhs = bin_op.lhs, |
| 2451 | | .rhs = bin_op.rhs, |
| 2452 | | }); |
| 2453 | | }, |
| 2452 | .bit_and => try self.bitwise(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), |
| 2453 | .bit_or => try self.bitwise(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), |
| 2454 | .xor => try self.bitwise(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), |
| 2455 | |
| 2456 | .shl_exact => try self.shiftExact(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), |
| 2457 | .shr_exact => try self.shiftExact(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), |
| 2458 | |
| 2459 | .shl => try self.shiftNormal(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), |
| 2460 | .shr => try self.shiftNormal(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), |
| 2461 | |
| 2462 | .bool_and => try self.booleanOp(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), |
| 2463 | .bool_or => try self.booleanOp(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), |
| 2464 | |
| 2465 | else => unreachable, |
| 2454 | 2466 | }; |
| 2455 | 2467 | }; |
| 2456 | 2468 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |