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.unary_op; 5 6 import vox.all; 7 8 9 @(AstType.expr_un_op) 10 struct UnaryExprNode { 11 mixin ExpressionNodeData!(AstType.expr_un_op); 12 UnOp op; 13 AstIndex child; 14 } 15 16 void print_unary_op(UnaryExprNode* node, ref AstPrintState state) 17 { 18 if (node.type) state.print("UNOP ", node.type.printer(state.context), " ", node.op); 19 else state.print("UNOP ", node.op); 20 print_ast(node.child, state); 21 } 22 23 void post_clone_unary_op(UnaryExprNode* node, ref CloneState state) 24 { 25 state.fixAstIndex(node.child); 26 } 27 28 void name_register_nested_unary_op(UnaryExprNode* node, ref NameRegisterState state) { 29 node.state = AstNodeState.name_register_nested; 30 require_name_register(node.child, state); 31 node.state = AstNodeState.name_register_nested_done; 32 } 33 34 void name_resolve_unary_op(UnaryExprNode* node, ref NameResolveState state) { 35 node.state = AstNodeState.name_resolve; 36 require_name_resolve(node.child, state); 37 switch(node.op) with(UnOp) 38 { 39 case addrOf: 40 AstNode* child = node.child.get_node(state.context); 41 child.flags |= AstFlags.isLvalue; 42 if (child.astType == AstType.expr_name_use) 43 { 44 child.flags |= NameUseFlags.forbidParenthesesFreeCall; 45 } 46 break; 47 default: 48 break; 49 } 50 node.state = AstNodeState.name_resolve_done; 51 } 52 53 void type_check_unary_op(UnaryExprNode* node, ref TypeCheckState state) 54 { 55 CompilationContext* c = state.context; 56 57 node.state = AstNodeState.type_check; 58 require_type_check(node.child, state); 59 ExpressionNode* child = node.child.get_expr(c); 60 assert(child.type, format("child(%s).type: is null", child.astType)); 61 62 if (child.type.isErrorType) 63 { 64 node.type = child.type; 65 node.state = AstNodeState.type_check_done; 66 return; 67 } 68 69 switch(node.op) with(UnOp) 70 { 71 case addrOf: 72 // make sure that variable gets stored in memory 73 //writefln("addrOf %s", child.astType); 74 switch(child.astType) 75 { 76 case AstType.expr_name_use: 77 AstNode* entity = child.as!NameUseExprNode(c).entity.get_node(c); 78 79 switch (entity.astType) 80 { 81 case AstType.decl_var: 82 // TODO: fix test 205. We do not detect all cases where pointer of local var is taken 83 // For example taking address of some member is not detected, only direct address obtaining. 84 entity.flags |= VariableFlags.isAddressTaken; // mark variable 85 node.type = c.appendAst!PtrTypeNode(node.child.loc(c), CommonAstNodes.type_type, node.child.get_expr_type(c)); 86 break; 87 case AstType.decl_function: 88 node.type = c.appendAst!PtrTypeNode(node.child.loc(c), CommonAstNodes.type_type, node.child.get_expr_type(c)); 89 break; 90 default: 91 c.internal_error(node.loc, "Cannot take address of %s", entity.astType); 92 } 93 break; 94 case AstType.expr_index: 95 node.type = c.appendAst!PtrTypeNode(node.child.loc(c), CommonAstNodes.type_type, node.child.get_expr_type(c)); 96 break; 97 case AstType.expr_member: 98 node.type = c.appendAst!PtrTypeNode(node.child.loc(c), CommonAstNodes.type_type, node.child.get_expr_type(c)); 99 break; 100 default: 101 c.internal_error(node.loc, "Cannot take address of %s", child.astType); 102 } 103 break; 104 case bitwiseNot: 105 node.type = child.type; 106 break; 107 case logicalNot: 108 autoconvToBool(node.child, c); 109 node.type = CommonAstNodes.type_bool; 110 break; 111 case GENERIC_MINUS: 112 node.type = child.type; 113 TypeNode* nodeType = node.type.get_type(c); 114 if (nodeType.isFloat) 115 node.op = UnOp.FLT_MINUS; 116 else 117 node.op = UnOp.INT_MINUS; 118 break; 119 case preIncrement, postIncrement, preDecrement, postDecrement: 120 child.flags |= AstFlags.isLvalue; 121 node.type = child.type; 122 break; 123 case deref: 124 if (child.type.isErrorType) { 125 node.type = child.type; 126 break; 127 } 128 if (!child.type.isPointerType(c)) { 129 c.unrecoverable_error(node.loc, "Cannot dereference %s", child.type.printer(c)); 130 } 131 node.type = child.type.get_type(c).as_ptr.base; 132 break; 133 default: 134 c.internal_error("un op %s not implemented", node.op); 135 } 136 node.state = AstNodeState.type_check_done; 137 } 138 139 ExprValue ir_gen_expr_unary_op(ref IrGenState gen, IrIndex currentBlock, ref IrLabel nextStmt, UnaryExprNode* u) 140 { 141 CompilationContext* c = gen.context; 142 143 switch(u.op) with(UnOp) 144 { 145 case addrOf: 146 IrLabel afterChild = IrLabel(currentBlock); 147 ExprValue lval = ir_gen_expr(gen, u.child, currentBlock, afterChild); 148 currentBlock = afterChild.blockIndex; 149 ExprValue res = lval.addrOf(gen, u.loc, currentBlock); 150 gen.builder.addJumpToLabel(currentBlock, nextStmt); 151 return res; 152 153 case bitwiseNot: 154 IrLabel afterChild = IrLabel(currentBlock); 155 ExprValue lval = ir_gen_expr(gen, u.child, currentBlock, afterChild); 156 currentBlock = afterChild.blockIndex; 157 IrIndex rval = lval.rvalue(gen, u.loc, currentBlock); 158 ExtraInstrArgs extra = {type : u.type.gen_ir_type(c), argSize : u.type.typeArgSize(c) }; 159 IrIndex result = gen.builder.emitInstr!(IrOpcode.not)(currentBlock, extra, rval).result; 160 gen.builder.addJumpToLabel(currentBlock, nextStmt); 161 return ExprValue(result); 162 163 case logicalNot: 164 IrLabel afterChild = IrLabel(currentBlock); 165 IrIndex result = makeBoolValue(gen, cast(ExpressionNode*)u, currentBlock, afterChild); 166 currentBlock = afterChild.blockIndex; 167 gen.builder.addJumpToLabel(currentBlock, nextStmt); 168 return ExprValue(result); 169 170 case FLT_MINUS, INT_MINUS: 171 IrLabel afterChild = IrLabel(currentBlock); 172 ExprValue lval = ir_gen_expr(gen, u.child, currentBlock, afterChild); 173 currentBlock = afterChild.blockIndex; 174 IrIndex rval = lval.rvalue(gen, u.loc, currentBlock); 175 ExtraInstrArgs extra = {type : u.type.gen_ir_type(c), argSize : u.type.typeArgSize(c) }; 176 IrIndex result; 177 if (u.op == FLT_MINUS) 178 result = gen.builder.emitInstr!(IrOpcode.fneg)(currentBlock, extra, rval).result; 179 else 180 result = gen.builder.emitInstr!(IrOpcode.neg)(currentBlock, extra, rval).result; 181 gen.builder.addJumpToLabel(currentBlock, nextStmt); 182 return ExprValue(result); 183 184 case preIncrement, postIncrement, preDecrement, postDecrement: 185 IrOpcode opcode = IrOpcode.sub; 186 if (u.op == preIncrement || u.op == postIncrement) opcode = IrOpcode.add; 187 188 ExpressionNode* childExpr = u.child.get_expr(c); 189 190 IrLabel afterChild = IrLabel(currentBlock); 191 ExprValue lval = ir_gen_expr(gen, u.child, currentBlock, afterChild); 192 currentBlock = afterChild.blockIndex; 193 194 IrIndex increment = c.constants.add(makeIrType(IrBasicType.i32), 1); // integers increment by 1 195 TypeNode* childType = childExpr.type.get_type(c); 196 if (childType.isPointer) { // pointers increment by size of element 197 uint size = childType.as_ptr.base.require_type_size(c).size; 198 increment = c.constants.add(makeIrType(IrBasicType.i32), size); 199 } 200 201 IrArgSize argSize = childType.argSize(c); 202 IrIndex rval = lval.rvalue(gen, u.loc, currentBlock); 203 ExtraInstrArgs extra = { 204 opcode : opcode, 205 type : childType.gen_ir_type(c), 206 argSize : argSize 207 }; 208 IrIndex opResult = gen.builder.emitInstr!(IrOpcode.generic_binary)( 209 currentBlock, extra, rval, increment).result; 210 lval.store(gen, u.loc, currentBlock, opResult); 211 212 gen.builder.addJumpToLabel(currentBlock, nextStmt); 213 214 if (u.op == preIncrement || u.op == preDecrement) 215 return ExprValue(opResult); 216 else 217 return ExprValue(rval); 218 219 case deref: 220 IrLabel afterChild = IrLabel(currentBlock); 221 ExprValue lval = ir_gen_expr(gen, u.child, currentBlock, afterChild); 222 currentBlock = afterChild.blockIndex; 223 ExprValue res = lval.deref(gen, u.loc, currentBlock); 224 gen.builder.addJumpToLabel(currentBlock, nextStmt); 225 return res; 226 227 case staticArrayToSlice: 228 IrLabel afterChild = IrLabel(currentBlock); 229 ExpressionNode* childExpr = u.child.get_expr(c); 230 ExprValue lval = ir_gen_expr(gen, u.child, currentBlock, afterChild); 231 currentBlock = afterChild.blockIndex; 232 233 // pointer to first element 234 IrIndex ZERO = c.constants.addZeroConstant(makeIrType(IrBasicType.i32)); 235 IrIndex ptr = buildGEPEx(gen, u.loc, currentBlock, lval, ZERO, ZERO); 236 // array length 237 IrIndex length = c.constants.add(makeIrType(IrBasicType.i64), childExpr.type.get_type(c).as_static_array.length); 238 239 // combine into slice {u64, T*} 240 IrIndex resType = u.type.gen_ir_type(c); 241 ExtraInstrArgs extra = { type : resType }; 242 InstrWithResult res = gen.builder.emitInstr!(IrOpcode.create_aggregate)(currentBlock, extra, length, ptr); 243 return ExprValue(res.result); 244 245 default: 246 c.internal_error(u.loc, "un op %s not implemented", u.op); 247 } 248 } 249 250 void ir_gen_branch_unary_op(ref IrGenState gen, IrIndex currentBlock, ref IrLabel trueExit, ref IrLabel falseExit, UnaryExprNode* u) 251 { 252 CompilationContext* c = gen.context; 253 switch(u.op) with(UnOp) 254 { 255 case logicalNot: 256 ir_gen_branch(gen, u.child, currentBlock, falseExit, trueExit); 257 break; 258 259 default: c.internal_error(u.loc, "Opcode `%s` is not implemented", u.op); 260 } 261 } 262 263 enum UnOp : ubyte { 264 plus, // + 265 GENERIC_MINUS, // - 266 INT_MINUS, 267 FLT_MINUS, 268 269 logicalNot, // ! 270 bitwiseNot, // ~ 271 deref, // * 272 addrOf, // & 273 preIncrement, // ++x 274 preDecrement, // --x 275 postIncrement, // x++ 276 postDecrement, // x-- 277 staticArrayToSlice, 278 } 279 280 IrIndex calcUnOp(UnOp op, IrIndex child, CompilationContext* c) 281 { 282 IrConstant childCon = c.constants.get(child); 283 284 switch(op) 285 { 286 case UnOp.bitwiseNot: 287 IrBasicType basicType = childCon.type.basicType(c); 288 ulong result; 289 switch(basicType) with(IrBasicType) { 290 case i8: result = cast(ubyte) (~childCon.u32); break; 291 case i16: result = cast(ushort)(~childCon.u32); break; 292 case i32: result = cast(uint) (~childCon.u32); break; 293 case i64: result = cast(ulong) (~childCon.u64); break; 294 default: c.internal_error("Invalid constant type %s", basicType); 295 } 296 return c.constants.add(childCon.type, result); 297 298 case UnOp.logicalNot: 299 assert(childCon.type.isTypeBasic); 300 assert(childCon.type.typeIndex == IrBasicType.i8); 301 return c.constants.add(childCon.type, !childCon.i1); 302 303 default: 304 c.internal_error("Opcode `%s` is not implemented", op); 305 } 306 }