1 /// Copyright: Copyright (c) 2017-2019 Andrey Penechko. 2 /// License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0). 3 /// Authors: Andrey Penechko. 4 module vox.fe.ast.expr.binary_op; 5 6 import vox.all; 7 8 enum BinaryOpFlags : ushort 9 { 10 isAssignment = AstFlags.userFlag << 0, 11 } 12 13 @(AstType.expr_bin_op) 14 struct BinaryExprNode { 15 mixin ExpressionNodeData!(AstType.expr_bin_op, 0); 16 BinOp op; 17 AstIndex left; 18 AstIndex right; 19 20 bool isAssignment() { return cast(bool)(flags & BinaryOpFlags.isAssignment); } 21 } 22 23 void print_binary_op(BinaryExprNode* node, ref AstPrintState state) 24 { 25 if (node.type) state.print("BINOP ", node.type.printer(state.context), " ", node.op); 26 else state.print("BINOP ", node.op); 27 print_ast(node.left, state); 28 print_ast(node.right, state); 29 } 30 31 void post_clone_binary_op(BinaryExprNode* node, ref CloneState state) 32 { 33 state.fixAstIndex(node.left); 34 state.fixAstIndex(node.right); 35 } 36 37 void name_register_nested_binary_op(BinaryExprNode* node, ref NameRegisterState state) { 38 node.state = AstNodeState.name_register_nested; 39 require_name_register(node.left, state); 40 require_name_register(node.right, state); 41 node.state = AstNodeState.name_register_nested_done; 42 } 43 44 void name_resolve_binary_op(BinaryExprNode* node, ref NameResolveState state) { 45 node.state = AstNodeState.name_resolve; 46 require_name_resolve(node.left, state); 47 require_name_resolve(node.right, state); 48 node.state = AstNodeState.name_resolve_done; 49 } 50 51 void type_check_binary_op(BinaryExprNode* node, ref TypeCheckState state) 52 { 53 CompilationContext* c = state.context; 54 55 node.state = AstNodeState.type_check; 56 require_type_check(node.left, state); 57 require_type_check(node.right, state); 58 assert(node.left.get_expr_type(c), format("left(%s).type: is null", node.left.astType(c))); 59 assert(node.right.get_expr_type(c), format("right(%s).type: is null", node.right.astType(c))); 60 61 setResultType(node, c); 62 TypeNode* leftType = node.left.get_expr(c).type.get_type(c); 63 node.op = selectTypedOpcode(node.op, leftType.isSigned, leftType.isFloat); 64 node.state = AstNodeState.type_check_done; 65 } 66 67 ExprValue ir_gen_expr_binary_op(ref IrGenState gen, IrIndex currentBlock, ref IrLabel nextStmt, BinaryExprNode* b) 68 { 69 CompilationContext* c = gen.context; 70 71 if (b.op == BinOp.LOGIC_AND || b.op == BinOp.LOGIC_OR) 72 { 73 IrLabel afterChild = IrLabel(currentBlock); 74 IrIndex irValue = makeBoolValue(gen, cast(ExpressionNode*)b, currentBlock, afterChild); 75 currentBlock = afterChild.blockIndex; 76 gen.builder.addJumpToLabel(currentBlock, nextStmt); 77 return ExprValue(irValue); 78 } 79 80 if (b.isAssignment) 81 { 82 IrLabel afterLeft = IrLabel(currentBlock); 83 ExprValue leftLvalue = ir_gen_expr(gen, b.left, currentBlock, afterLeft); 84 currentBlock = afterLeft.blockIndex; 85 86 IrLabel afterRight = IrLabel(currentBlock); 87 ExprValue rightLvalue = ir_gen_expr(gen, b.right, currentBlock, afterRight); 88 currentBlock = afterRight.blockIndex; 89 IrIndex rightRvalue = rightLvalue.rvalue(gen, b.loc, currentBlock); 90 91 ExpressionNode* leftExpr = b.left.get_expr(c); 92 ExpressionNode* rightExpr = b.right.get_expr(c); 93 94 c.assertf(leftLvalue.irValue.isDefined, leftExpr.loc, "%s null IR val", leftExpr.astType); 95 c.assertf(rightLvalue.irValue.isDefined, rightExpr.loc, "%s null IR val", rightExpr.astType); 96 97 if (b.op == BinOp.ASSIGN) { 98 leftLvalue.store(gen, b.loc, currentBlock, rightRvalue); 99 gen.builder.addJumpToLabel(currentBlock, nextStmt); 100 return rightLvalue; 101 } 102 else if (b.op == BinOp.PTR_PLUS_INT_ASSIGN) 103 { 104 IrIndex leftRvalue = leftLvalue.rvalue(gen, b.loc, currentBlock); 105 assert(leftExpr.type.get_type(c).isPointer && rightExpr.type.get_type(c).isInteger); 106 IrIndex irValue = buildGEP(gen, b.loc, currentBlock, leftRvalue, rightRvalue); 107 leftLvalue.store(gen, b.loc, currentBlock, irValue); 108 gen.builder.addJumpToLabel(currentBlock, nextStmt); 109 return ExprValue(irValue); 110 } 111 else 112 { 113 IrIndex leftRvalue = leftLvalue.rvalue(gen, b.loc, currentBlock); 114 IrIndex irValue; 115 116 if (leftRvalue.isSimpleConstant && rightRvalue.isSimpleConstant) 117 { 118 irValue = calcBinOp(binOpAssignToRegularOp(b.op), leftRvalue, rightRvalue, c); 119 } 120 else 121 { 122 ExtraInstrArgs extra = { 123 opcode : binOpcode(b.op, b.loc, c), 124 type : leftExpr.type.gen_ir_type(c), 125 argSize : leftExpr.type.typeArgSize(c) 126 }; 127 irValue = gen.builder.emitInstr!(IrOpcode.generic_binary)( 128 currentBlock, extra, leftRvalue, rightRvalue).result; 129 } 130 131 leftLvalue.store(gen, b.loc, currentBlock, irValue); 132 gen.builder.addJumpToLabel(currentBlock, nextStmt); 133 return ExprValue(irValue); 134 } 135 } 136 else 137 { 138 IrLabel fake; 139 IrIndex irValue = visitBinOpImpl!true(gen, currentBlock, nextStmt, fake, b); 140 assert(fake.numPredecessors == 0); 141 return ExprValue(irValue); 142 } 143 } 144 145 void ir_gen_branch_binary_op(ref IrGenState gen, IrIndex currentBlock, ref IrLabel trueExit, ref IrLabel falseExit, BinaryExprNode* b) 146 { 147 CompilationContext* c = gen.context; 148 if (b.isAssignment) 149 { 150 c.error(b.loc, "Cannot assign inside condition"); 151 } 152 if (b.op == BinOp.LOGIC_AND) 153 { 154 IrLabel cond2Label = IrLabel(currentBlock); 155 ir_gen_branch(gen, b.left, currentBlock, cond2Label, falseExit); 156 157 if (cond2Label.numPredecessors != 0) 158 { 159 IrIndex cond2Block = cond2Label.blockIndex; 160 gen.builder.sealBlock(cond2Block); 161 ir_gen_branch(gen, b.right, cond2Block, trueExit, falseExit); 162 } 163 164 return; 165 } 166 else if (b.op == BinOp.LOGIC_OR) 167 { 168 IrLabel cond2Label = IrLabel(currentBlock); 169 ir_gen_branch(gen, b.left, currentBlock, trueExit, cond2Label); 170 171 if (cond2Label.numPredecessors != 0) 172 { 173 IrIndex cond2Block = cond2Label.blockIndex; 174 gen.builder.sealBlock(cond2Block); 175 ir_gen_branch(gen, b.right, cond2Block, trueExit, falseExit); 176 } 177 178 return; 179 } 180 visitBinOpImpl!false(gen, currentBlock, trueExit, falseExit, b); 181 } 182 183 // In value mode only uses trueExit as nextStmt 184 IrIndex visitBinOpImpl(bool forValue)(ref IrGenState gen, ref IrIndex currentBlock, ref IrLabel trueExit, ref IrLabel falseExit, BinaryExprNode* b) 185 { 186 CompilationContext* c = gen.context; 187 188 IrLabel afterLeft = IrLabel(currentBlock); 189 ExprValue leftLvalue = ir_gen_expr(gen, b.left, currentBlock, afterLeft); 190 currentBlock = afterLeft.blockIndex; 191 192 IrLabel afterRight = IrLabel(currentBlock); 193 ExprValue rightLvalue = ir_gen_expr(gen, b.right, currentBlock, afterRight); 194 currentBlock = afterRight.blockIndex; 195 196 ExpressionNode* leftExpr = b.left.get_expr(c); 197 ExpressionNode* rightExpr = b.right.get_expr(c); 198 199 c.assertf(leftLvalue.irValue.isDefined, leftExpr.loc, "%s null IR val", leftExpr.astType); 200 c.assertf(rightLvalue.irValue.isDefined, rightExpr.loc, "%s null IR val", rightExpr.astType); 201 202 auto leftValue = leftLvalue.rvalue(gen, b.loc, currentBlock); 203 auto rightValue = rightLvalue.rvalue(gen, b.loc, currentBlock); 204 205 // constant folding 206 if (leftValue.isSimpleConstant && rightValue.isSimpleConstant) 207 { 208 IrIndex value = calcBinOp(b.op, leftValue, rightValue, c); 209 static if (forValue) 210 { 211 c.assertf(value.isDefined, b.loc, "%s null IR val", b.astType); 212 return value; 213 } 214 else 215 { 216 if (c.constants.get(value).i8) 217 gen.builder.addJumpToLabel(currentBlock, trueExit); 218 else 219 gen.builder.addJumpToLabel(currentBlock, falseExit); 220 return IrIndex(); 221 } 222 } 223 224 static if (forValue) 225 { 226 IrIndex resType = b.type.gen_ir_type(c); 227 IrIndex irValue; 228 switch(b.op) with(BinOp) 229 { 230 case EQUAL, NOT_EQUAL: 231 case SGT, SGE, SLT, SLE: 232 case UGT, UGE, ULT, ULE: 233 case FGT, FGE, FLT, FLE: 234 ExtraInstrArgs extra = { cond : convertBinOpToIrCond(b.op), type : resType }; 235 irValue = gen.builder.emitInstr!(IrOpcode.set_binary_cond)(currentBlock, extra, leftValue, rightValue).result; 236 break; 237 238 case PTR_PLUS_INT: 239 assert(leftExpr.type.get_type(c).isPointer && rightExpr.type.get_type(c).isInteger); 240 irValue = buildGEP(gen, b.loc, currentBlock, leftValue, rightValue); 241 break; 242 243 case PTR_DIFF: 244 assert(leftExpr.type.get_type(c).isPointer && rightExpr.type.get_type(c).isPointer); 245 246 ExtraInstrArgs extra = { type : resType, argSize : leftExpr.type.typeArgSize(c) }; 247 irValue = gen.builder.emitInstr!(IrOpcode.sub)(currentBlock, extra, leftValue, rightValue).result; 248 249 // divide by elem size 250 TypeNode* baseType = leftExpr.type.get_type(c).as_ptr.base.get_type(c); 251 uint elemSize = baseType.sizealign(c).size; 252 if (elemSize == 1 || baseType.isVoid) break; 253 254 ExtraInstrArgs extra2 = { type : resType, argSize : leftExpr.type.typeArgSize(c) }; 255 IrIndex elemSizeValue = c.constants.add(resType, elemSize); 256 irValue = gen.builder.emitInstr!(IrOpcode.sdiv)(currentBlock, extra2, irValue, elemSizeValue).result; 257 break; 258 259 case INT_PLUS, INT_MINUS, INT_SMUL, INT_UMUL, INT_SDIV, INT_UDIV, INT_SREM, INT_UREM: 260 case FLT_PLUS, FLT_MINUS, FLT_MUL, FLT_DIV: 261 case SHL, SHR, ASHR, XOR, BITWISE_AND, BITWISE_OR: 262 ExtraInstrArgs extra = { 263 opcode : binOpcode(b.op, b.loc, c), 264 type : resType, 265 argSize : b.type.typeArgSize(c) 266 }; 267 irValue = gen.builder.emitInstr!(IrOpcode.generic_binary)( 268 currentBlock, extra, leftValue, rightValue).result; 269 break; 270 default: c.internal_error(b.loc, "Opcode `%s` is not implemented", b.op); 271 } 272 c.assertf(irValue.isDefined, b.loc, "%s null IR val", b.astType); 273 gen.builder.addJumpToLabel(currentBlock, trueExit); 274 return irValue; 275 } 276 else // branch 277 { 278 switch(b.op) with(BinOp) 279 { 280 case EQUAL, NOT_EQUAL: 281 case SGT, SGE, SLT, SLE: 282 case UGT, UGE, ULT, ULE: 283 case FGT, FGE, FLT, FLE: 284 auto branch = gen.builder.addBinBranch( 285 currentBlock, convertBinOpToIrCond(b.op), leftExpr.type.typeArgSize(c), 286 leftValue, rightValue, trueExit, falseExit); 287 break; 288 default: c.internal_error(b.loc, "Opcode `%s` is not implemented", b.op); 289 } 290 return IrIndex(); 291 } 292 } 293 294 IrOpcode binOpcode(BinOp binop, TokenIndex loc, CompilationContext* context) 295 { 296 switch(binop) with(BinOp) 297 { 298 case INT_PLUS, INT_PLUS_ASSIGN: return IrOpcode.add; 299 case INT_MINUS, INT_MINUS_ASSIGN: return IrOpcode.sub; 300 case INT_SMUL, INT_SMUL_ASSIGN: return IrOpcode.smul; 301 case INT_UMUL, INT_UMUL_ASSIGN: return IrOpcode.umul; 302 case INT_SDIV, INT_SDIV_ASSIGN: return IrOpcode.sdiv; 303 case INT_UDIV, INT_UDIV_ASSIGN: return IrOpcode.udiv; 304 case INT_SREM, INT_SREM_ASSIGN: return IrOpcode.srem; 305 case INT_UREM, INT_UREM_ASSIGN: return IrOpcode.urem; 306 307 case FLT_PLUS, FLT_PLUS_ASSIGN: return IrOpcode.fadd; 308 case FLT_MINUS, FLT_MINUS_ASSIGN: return IrOpcode.fsub; 309 case FLT_DIV, FLT_DIV_ASSIGN: return IrOpcode.fdiv; 310 case FLT_MUL, FLT_MUL_ASSIGN: return IrOpcode.fmul; 311 312 case SHL, SHL_ASSIGN: return IrOpcode.shl; 313 case SHR, SHR_ASSIGN: return IrOpcode.lshr; 314 case ASHR, ASHR_ASSIGN: return IrOpcode.ashr; 315 case XOR, XOR_ASSIGN: return IrOpcode.xor; 316 case BITWISE_AND, BITWISE_AND_ASSIGN: return IrOpcode.and; 317 case BITWISE_OR, BITWISE_OR_ASSIGN: return IrOpcode.or; 318 default: 319 context.internal_error(loc, "assign op %s not implemented", binop); 320 } 321 } 322 323 IrIndex calcBinOp(BinOp op, IrIndex left, IrIndex right, CompilationContext* c) 324 { 325 c.assertf(left.isSimpleConstant, "%s is not a constant", left); 326 c.assertf(right.isSimpleConstant, "%s is not a constant", right); 327 328 IrConstant leftCon = c.constants.get(left); 329 IrConstant rightCon = c.constants.get(right); 330 331 bool is_f64() { return leftCon.type == makeIrType(IrBasicType.f64); } 332 333 switch(op) 334 { 335 case BinOp.EQUAL: return c.constants.add(makeIrType(IrBasicType.i8), cast(ubyte)(leftCon.i64 == rightCon.i64)); 336 case BinOp.NOT_EQUAL: return c.constants.add(makeIrType(IrBasicType.i8), cast(ubyte)(leftCon.i64 != rightCon.i64)); 337 338 case BinOp.SGT: return c.constants.add(makeIrType(IrBasicType.i8), cast(ubyte)(leftCon.i64 > rightCon.i64)); 339 case BinOp.SGE: return c.constants.add(makeIrType(IrBasicType.i8), cast(ubyte)(leftCon.i64 >= rightCon.i64)); 340 case BinOp.SLT: return c.constants.add(makeIrType(IrBasicType.i8), cast(ubyte)(leftCon.i64 < rightCon.i64)); 341 case BinOp.SLE: return c.constants.add(makeIrType(IrBasicType.i8), cast(ubyte)(leftCon.i64 <= rightCon.i64)); 342 343 case BinOp.UGT: return c.constants.add(makeIrType(IrBasicType.i8), cast(ubyte)(leftCon.u64 > rightCon.u64)); 344 case BinOp.UGE: return c.constants.add(makeIrType(IrBasicType.i8), cast(ubyte)(leftCon.u64 >= rightCon.u64)); 345 case BinOp.ULT: return c.constants.add(makeIrType(IrBasicType.i8), cast(ubyte)(leftCon.u64 < rightCon.u64)); 346 case BinOp.ULE: return c.constants.add(makeIrType(IrBasicType.i8), cast(ubyte)(leftCon.u64 <= rightCon.u64)); 347 348 case BinOp.FGT: if (is_f64) return c.constants.add(makeIrType(IrBasicType.i8), cast(ubyte)(leftCon.f64 > rightCon.f64)); return c.constants.add(makeIrType(IrBasicType.i8), cast(ubyte)(leftCon.f32 > rightCon.f32)); 349 case BinOp.FGE: if (is_f64) return c.constants.add(makeIrType(IrBasicType.i8), cast(ubyte)(leftCon.f64 >= rightCon.f64)); return c.constants.add(makeIrType(IrBasicType.i8), cast(ubyte)(leftCon.f32 >= rightCon.f32)); 350 case BinOp.FLT: if (is_f64) return c.constants.add(makeIrType(IrBasicType.i8), cast(ubyte)(leftCon.f64 < rightCon.f64)); return c.constants.add(makeIrType(IrBasicType.i8), cast(ubyte)(leftCon.f32 < rightCon.f32)); 351 case BinOp.FLE: if (is_f64) return c.constants.add(makeIrType(IrBasicType.i8), cast(ubyte)(leftCon.f64 <= rightCon.f64)); return c.constants.add(makeIrType(IrBasicType.i8), cast(ubyte)(leftCon.f32 <= rightCon.f32)); 352 353 case BinOp.INT_PLUS: return c.constants.add(leftCon.type, leftCon.i64 + rightCon.i64); 354 case BinOp.FLT_PLUS: 355 if (is_f64) return c.constants.add(leftCon.f64 + rightCon.f64); 356 return c.constants.add(leftCon.f32 + rightCon.f32); 357 case BinOp.INT_MINUS: 358 return c.constants.add(leftCon.type, leftCon.i64 - rightCon.i64); 359 case BinOp.FLT_MINUS: 360 if (is_f64) return c.constants.add(leftCon.f64 - rightCon.f64); 361 return c.constants.add(leftCon.f32 - rightCon.f32); 362 case BinOp.INT_SMUL: return c.constants.add(leftCon.type, leftCon.i64 * rightCon.i64); 363 case BinOp.INT_UMUL: return c.constants.add(leftCon.type, leftCon.u64 * rightCon.u64); 364 case BinOp.FLT_MUL: 365 if (is_f64) return c.constants.add(leftCon.f64 * rightCon.f64); 366 return c.constants.add(leftCon.f32 * rightCon.f32); 367 case BinOp.INT_SDIV: return c.constants.add(leftCon.type, leftCon.i64 / rightCon.i64); 368 case BinOp.INT_UDIV: return c.constants.add(leftCon.type, leftCon.u64 / rightCon.u64); 369 case BinOp.FLT_DIV: 370 if (is_f64) return c.constants.add(leftCon.f64 / rightCon.f64); 371 return c.constants.add(leftCon.f32 / rightCon.f32); 372 373 case BinOp.INT_SREM: return c.constants.add(leftCon.type, leftCon.i64 % rightCon.i64); 374 case BinOp.INT_UREM: return c.constants.add(leftCon.type, leftCon.u64 % rightCon.u64); 375 376 case BinOp.SHL: 377 IrBasicType basicType = leftCon.type.basicType(c); 378 ulong result; 379 switch(basicType) with(IrBasicType) { 380 case i8: result = leftCon.i32 << rightCon.i64; break; // (leftCon.i8 << rightCon.i64 & 0b111)) & 0xFF; break; 381 case i16: result = leftCon.i32 << rightCon.i64; break; // (leftCon.i16 << rightCon.i64 & 0b1111)) & 0xFFFF; break; 382 case i32: result = leftCon.i32 << rightCon.i64; break; // (leftCon.i32 << rightCon.i64 & 0b11111)) & 0xFFFF_FFFF; break; 383 case i64: result = leftCon.i64 << rightCon.i64; break; // (leftCon.i64 << rightCon.i64 & 0b111111)) & 0xFFFF_FFFF_FFFF_FFFF; break; 384 default: c.internal_error("Invalid constant type %s", basicType); 385 } 386 return c.constants.add(leftCon.type, result); 387 case BinOp.SHR: 388 IrBasicType basicType = leftCon.type.basicType(c); 389 ulong result; 390 switch(basicType) with(IrBasicType) { 391 case i8: result = leftCon.i32 >>> rightCon.i64; break; // (leftCon.i8 >>> (rightCon.i64 & 0b111)) & 0xFF; break; 392 case i16: result = leftCon.i32 >>> rightCon.i64; break; // (leftCon.i16 >>> (rightCon.i64 & 0b1111)) & 0xFFFF; break; 393 case i32: result = leftCon.i32 >>> rightCon.i64; break; // (leftCon.i32 >>> (rightCon.i64 & 0b11111)) & 0xFFFF_FFFF; break; 394 case i64: result = leftCon.i64 >>> rightCon.i64; break; // (leftCon.i64 >>> (rightCon.i64 & 0b111111)) & 0xFFFF_FFFF_FFFF_FFFF; break; 395 default: c.internal_error("Invalid constant type %s", basicType); 396 } 397 return c.constants.add(leftCon.type, result); 398 case BinOp.ASHR: 399 IrBasicType basicType = leftCon.type.basicType(c); 400 ulong result; 401 switch(basicType) with(IrBasicType) { 402 case i8: result = leftCon.i32 >> rightCon.i64; break; // (leftCon.i8 >> (rightCon.i64 & 0b111)) & 0xFF; break; 403 case i16: result = leftCon.i32 >> rightCon.i64; break; // (leftCon.i16 >> (rightCon.i64 & 0b1111)) & 0xFFFF; break; 404 case i32: result = leftCon.i32 >> rightCon.i64; break; // (leftCon.i32 >> (rightCon.i64 & 0b11111)) & 0xFFFF_FFFF; break; 405 case i64: result = leftCon.i64 >> rightCon.i64; break; // (leftCon.i64 >> (rightCon.i64 & 0b111111)) & 0xFFFF_FFFF_FFFF_FFFF; break; 406 default: c.internal_error("Invalid constant type %s", basicType); 407 } 408 return c.constants.add(leftCon.type, result); 409 410 case BinOp.BITWISE_OR: return c.constants.add(leftCon.type, leftCon.i64 | rightCon.i64); 411 case BinOp.BITWISE_AND: return c.constants.add(leftCon.type, leftCon.i64 & rightCon.i64); 412 case BinOp.XOR: return c.constants.add(leftCon.type, leftCon.i64 ^ rightCon.i64); 413 414 default: c.internal_error("Opcode `%s` is not implemented", op); 415 } 416 } 417 418 IrBinaryCondition convertBinOpToIrCond(BinOp op) 419 { 420 switch(op) with(BinOp) with(IrBinaryCondition) 421 { 422 case EQUAL: return eq; 423 case NOT_EQUAL: return ne; 424 case SGT: return sgt; 425 case SGE: return sge; 426 case SLT: return slt; 427 case SLE: return sle; 428 case UGT: return ugt; 429 case UGE: return uge; 430 case ULT: return ult; 431 case ULE: return ule; 432 case FGT: return fgt; 433 case FGE: return fge; 434 case FLT: return flt; 435 case FLE: return fle; 436 default: assert(false, "Unexpected BinOp"); 437 } 438 } 439 440 void setResultType(BinaryExprNode* b, CompilationContext* c) 441 { 442 AstIndex resType = CommonAstNodes.type_error; 443 b.type = resType; 444 AstIndex leftTypeIndex = b.left.get_expr_type(c); 445 TypeNode* leftType = leftTypeIndex.get_type(c); 446 AstIndex rightTypeIndex = b.right.get_expr_type(c); 447 TypeNode* rightType = rightTypeIndex.get_type(c); 448 449 if (leftType.astType == AstType.decl_enum) { 450 leftTypeIndex = leftType.as_enum.memberType; 451 leftType = leftTypeIndex.get_type(c); 452 } 453 if (rightType.astType == AstType.decl_enum) { 454 rightTypeIndex = rightType.as_enum.memberType; 455 rightType = rightTypeIndex.get_type(c); 456 } 457 458 if (leftTypeIndex.isErrorType || rightTypeIndex.isErrorType) return; 459 460 switch(b.op) with(BinOp) 461 { 462 // logic ops. Requires both operands to be bool 463 case LOGIC_AND, LOGIC_OR: 464 autoconvToBool(b.left, c); 465 autoconvToBool(b.right, c); 466 resType = CommonAstNodes.type_bool; 467 break; 468 // logic ops. Requires both operands to be of the same type 469 case EQUAL, NOT_EQUAL, GENERIC_GREATER, GENERIC_GREATER_EQUAL, GENERIC_LESS, GENERIC_LESS_EQUAL: 470 if (leftType.isPointer && rightType.isPointer) 471 { 472 if ( 473 same_type(leftTypeIndex, rightTypeIndex, c) || 474 leftType.as_ptr.isVoidPtr(c) || 475 rightType.as_ptr.isVoidPtr(c)) 476 { 477 resType = CommonAstNodes.type_bool; 478 break; 479 } 480 } 481 482 if (autoconvToCommonType(b.left, b.right, c)) { 483 resType = CommonAstNodes.type_bool; 484 } 485 else 486 c.error(b.loc, "Cannot compare `%s` and `%s`", 487 leftType.typeName(c), 488 rightType.typeName(c)); 489 break; 490 491 case GENERIC_MINUS: 492 if (leftType.isPointer && rightType.isPointer) // handle ptr - ptr 493 { 494 if (same_type(leftTypeIndex, rightTypeIndex, c)) 495 { 496 b.op = BinOp.PTR_DIFF; 497 resType = CommonAstNodes.type_i64; 498 break; 499 } 500 else 501 { 502 c.error(b.loc, "cannot subtract pointers to different types: `%s` and `%s`", 503 leftType.printer(c), rightType.printer(c)); 504 break; 505 } 506 } else if (leftType.isPointer && rightType.isInteger) { // handle ptr - int 507 b.op = BinOp.PTR_PLUS_INT; 508 (cast(IntLiteralExprNode*)b.right.get_node(c)).negate(b.loc, *c); 509 resType = leftTypeIndex; 510 break; 511 } 512 goto case GENERIC_DIV; 513 514 case GENERIC_PLUS: 515 // handle int + ptr and ptr + int 516 if (leftType.isPointer && rightType.isInteger) { 517 b.op = BinOp.PTR_PLUS_INT; 518 resType = leftTypeIndex; 519 break; 520 } else if (leftType.isInteger && rightType.isPointer) { 521 b.op = BinOp.PTR_PLUS_INT; 522 // canonicalize 523 swap(b.left, b.right); 524 resType = leftTypeIndex; 525 break; 526 } 527 528 goto case GENERIC_DIV; 529 530 // arithmetic op int float 531 case GENERIC_DIV, GENERIC_MUL: 532 if (autoconvToCommonType(b.left, b.right, c)) 533 { 534 resType = b.left.get_expr_type(c); 535 } 536 else 537 { 538 c.error(b.loc, "Cannot perform `%s` %s `%s` operation", 539 leftType.typeName(c), binOpStrings[b.op], 540 rightType.typeName(c)); 541 } 542 break; 543 544 // integer only 545 case GENERIC_INT_REM, SHL, SHR, ASHR, BITWISE_AND, BITWISE_OR, XOR: 546 if (leftType.isInteger && rightType.isInteger && autoconvToCommonType(b.left, b.right, c)) 547 { 548 resType = b.left.get_expr_type(c); 549 } 550 else 551 { 552 c.error(b.loc, "Cannot perform `%s` %s `%s` operation", 553 leftType.typeName(c), binOpStrings[b.op], 554 rightType.typeName(c)); 555 } 556 break; 557 558 case GENERIC_MINUS_ASSIGN: 559 if (leftType.isPointer && rightType.isInteger) { 560 b.op = BinOp.PTR_PLUS_INT_ASSIGN; 561 (cast(IntLiteralExprNode*)b.right.get_node(c)).negate(b.loc, *c); 562 resType = leftTypeIndex; 563 break; 564 } 565 goto case BITWISE_AND_ASSIGN; 566 567 case GENERIC_PLUS_ASSIGN: 568 if (leftType.isPointer && rightType.isInteger) { 569 b.op = BinOp.PTR_PLUS_INT_ASSIGN; 570 resType = leftTypeIndex; 571 break; 572 } 573 goto case BITWISE_AND_ASSIGN; 574 575 case GENERIC_DIV_ASSIGN, GENERIC_MUL_ASSIGN: 576 bool success = autoconvTo(b.right, leftTypeIndex, c); 577 if (!success) 578 c.error(b.loc, "Cannot perform `%s` %s `%s` operation", 579 leftType.typeName(c), binOpStrings[b.op], 580 rightType.typeName(c)); 581 resType = CommonAstNodes.type_void; 582 break; 583 584 case BITWISE_AND_ASSIGN, BITWISE_OR_ASSIGN, GENERIC_INT_REM_ASSIGN, 585 SHL_ASSIGN, SHR_ASSIGN, ASHR_ASSIGN, XOR_ASSIGN: 586 bool success = leftType.isInteger && rightType.isInteger && autoconvTo(b.right, leftTypeIndex, c); 587 if (!success) 588 c.error(b.loc, "Cannot perform `%s` %s `%s` operation", 589 leftType.typeName(c), binOpStrings[b.op], 590 rightType.typeName(c)); 591 resType = CommonAstNodes.type_void; 592 break; 593 594 case ASSIGN: 595 bool success = autoconvTo(b.right, leftTypeIndex, c); 596 if (!success) 597 c.error(b.loc, "Cannot perform `%s` %s `%s` operation", 598 leftType.typeName(c), binOpStrings[b.op], 599 rightType.typeName(c)); 600 resType = CommonAstNodes.type_void; 601 break; 602 603 default: 604 c.internal_error(b.loc, "Unimplemented op %s", b.op); 605 } 606 assert(resType.isDefined); 607 b.type = resType; 608 } 609 610 BinOp selectTypedOpcode(BinOp op, bool isSigned, bool isFloat) 611 { 612 switch(op) with(BinOp) { 613 case GENERIC_GREATER: 614 if (isFloat) return FGT; 615 if (isSigned) return SGT; 616 return UGT; 617 case GENERIC_GREATER_EQUAL: 618 if (isFloat) return FGE; 619 if (isSigned) return SGE; 620 return UGE; 621 case GENERIC_LESS: 622 if (isFloat) return FLT; 623 if (isSigned) return SLT; 624 return ULT; 625 case GENERIC_LESS_EQUAL: 626 if (isFloat) return FLE; 627 if (isSigned) return SLE; 628 return ULE; 629 case GENERIC_INT_REM: 630 if (isSigned) return INT_SREM; 631 return INT_UREM; 632 case GENERIC_PLUS: 633 if (isFloat) return FLT_PLUS; 634 return INT_PLUS; 635 case GENERIC_MINUS: 636 if (isFloat) return FLT_MINUS; 637 return INT_MINUS; 638 case GENERIC_MUL: 639 if (isFloat) return FLT_MUL; 640 if (isSigned) return INT_SMUL; 641 return INT_UMUL; 642 case GENERIC_DIV: 643 if (isFloat) return FLT_DIV; 644 if (isSigned) return INT_SDIV; 645 return INT_UDIV; 646 case GENERIC_PLUS_ASSIGN: 647 if (isFloat) return FLT_PLUS_ASSIGN; 648 return INT_PLUS_ASSIGN; 649 case GENERIC_MINUS_ASSIGN: 650 if (isFloat) return FLT_MINUS_ASSIGN; 651 return INT_MINUS_ASSIGN; 652 case GENERIC_MUL_ASSIGN: 653 if (isFloat) return FLT_MUL_ASSIGN; 654 if (isSigned) return INT_SMUL_ASSIGN; 655 return INT_UMUL_ASSIGN; 656 case GENERIC_DIV_ASSIGN: 657 if (isFloat) return FLT_DIV_ASSIGN; 658 if (isSigned) return INT_SDIV_ASSIGN; 659 return INT_UDIV_ASSIGN; 660 case GENERIC_INT_REM_ASSIGN: 661 if (isSigned) return INT_SREM_ASSIGN; 662 return INT_UREM_ASSIGN; 663 default: return op; 664 } 665 } 666 667 BinOp binOpAssignToRegularOp(BinOp op) { 668 switch(op) with(BinOp) { 669 case BITWISE_AND_ASSIGN: return BITWISE_AND; 670 case BITWISE_OR_ASSIGN: return BITWISE_OR; 671 case XOR_ASSIGN: return XOR; 672 case SHL_ASSIGN: return SHL; 673 case SHR_ASSIGN: return SHR; 674 case ASHR_ASSIGN: return ASHR; 675 case INT_PLUS_ASSIGN: return INT_PLUS; 676 case INT_MINUS_ASSIGN: return INT_MINUS; 677 case INT_SMUL_ASSIGN: return INT_SMUL; 678 case INT_UMUL_ASSIGN: return INT_UMUL; 679 case INT_SDIV_ASSIGN: return INT_SDIV; 680 case INT_UDIV_ASSIGN: return INT_UDIV; 681 case INT_SREM_ASSIGN: return INT_SREM; 682 case INT_UREM_ASSIGN: return INT_UREM; 683 case FLT_PLUS_ASSIGN: return FLT_PLUS; 684 case FLT_MINUS_ASSIGN: return FLT_MINUS; 685 case FLT_MUL_ASSIGN: return FLT_MUL; 686 case FLT_DIV_ASSIGN: return FLT_DIV; 687 case PTR_PLUS_INT_ASSIGN: return PTR_PLUS_INT; 688 default: assert(false); 689 } 690 } 691 692 693 /// 694 string[] binOpStrings = gatherStrings!BinOp; 695 696 enum BinOp : ubyte { 697 // logic ops 698 @("&&") LOGIC_AND, // && 699 @("||") LOGIC_OR, // || 700 701 // comparisons are converted into IrBinaryCondition, order is important 702 @("==") EQUAL, // == 703 @("!=") NOT_EQUAL, // != 704 705 // generic compare 706 @(">") GENERIC_GREATER, // > 707 @(">=") GENERIC_GREATER_EQUAL, // >= 708 @("<") GENERIC_LESS, // < 709 @("<=") GENERIC_LESS_EQUAL, // <= 710 711 // integer compare 712 // signed 713 @("s>") SGT, // > 714 @("s>=") SGE, // >= 715 @("s<") SLT, // < 716 @("s<=") SLE, // <= 717 718 // unsigned 719 @("u>") UGT, // > 720 @("u>=") UGE, // >= 721 @("u<") ULT, // < 722 @("u<=") ULE, // <= 723 724 // float compare 725 @("f>") FGT, // > 726 @("f>=") FGE, // >= 727 @("f<") FLT, // < 728 @("f<=") FLE, // <= 729 730 // arithmetic ops 731 @("&") BITWISE_AND, // & 732 @("|") BITWISE_OR, // | 733 @("^") XOR, // ^ 734 735 @("<<") SHL, // << 736 @(">>") SHR, // >> 737 @(">>>") ASHR, // >>> 738 739 @("+") GENERIC_PLUS, // + 740 @("-") GENERIC_MINUS, // - 741 @("*") GENERIC_MUL, // * 742 @("/") GENERIC_DIV, // / 743 @("%") GENERIC_INT_REM, // % 744 745 @("+") FLT_PLUS, // + 746 @("-") FLT_MINUS, // - 747 @("*") FLT_MUL, // * 748 @("/") FLT_DIV, // / 749 750 @("+") INT_PLUS, // + 751 @("-") INT_MINUS, // - 752 @("*") INT_SMUL, // * 753 @("*") INT_UMUL, // * 754 @("/") INT_SDIV, // / 755 @("/") INT_UDIV, // / 756 @("%") INT_SREM, // % 757 @("%") INT_UREM, // % 758 759 @("-") PTR_DIFF, // ptr - ptr 760 @("+") PTR_PLUS_INT, // ptr + int and ptr - int 761 762 @("=") ASSIGN, // = 763 764 @("&=") BITWISE_AND_ASSIGN, // &= 765 @("|=") BITWISE_OR_ASSIGN, // |= 766 @("^=") XOR_ASSIGN, // ^= 767 768 @("<<=") SHL_ASSIGN, // <<= 769 @(">>=") SHR_ASSIGN, // >>= 770 @(">>>=") ASHR_ASSIGN, // >>>= 771 772 773 @("+=") GENERIC_PLUS_ASSIGN, // += 774 @("-=") GENERIC_MINUS_ASSIGN, // -= 775 @("*=") GENERIC_MUL_ASSIGN, // *= 776 @("/=") GENERIC_DIV_ASSIGN, // /= 777 @("%=") GENERIC_INT_REM_ASSIGN, // %= 778 779 @("+=") INT_PLUS_ASSIGN, // += 780 @("-=") INT_MINUS_ASSIGN, // -= 781 @("*=") INT_SMUL_ASSIGN, // *= 782 @("*=") INT_UMUL_ASSIGN, // *= 783 @("/=") INT_SDIV_ASSIGN, // /= 784 @("/=") INT_UDIV_ASSIGN, // /= 785 @("%=") INT_SREM_ASSIGN, // %= 786 @("%=") INT_UREM_ASSIGN, // %= 787 788 @("+=") FLT_PLUS_ASSIGN, // += 789 @("-=") FLT_MINUS_ASSIGN, // -= 790 @("*=") FLT_MUL_ASSIGN, // *= 791 @("/=") FLT_DIV_ASSIGN, // /= 792 793 @("+") PTR_PLUS_INT_ASSIGN,// ptr -= / += int 794 795 // member access 796 @(".") DOT, // . 797 } 798 799 private string[] gatherStrings(alias _enum)() 800 { 801 string[] res = new string[__traits(allMembers, _enum).length]; 802 foreach (i, m; __traits(allMembers, _enum)) 803 { 804 res[i] = __traits(getAttributes, __traits(getMember, _enum, m))[0]; 805 } 806 return res; 807 }