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 }