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.stmt.for_stmt;
5 
6 import vox.all;
7 
8 
9 @(AstType.stmt_for)
10 struct ForStmtNode {
11 	mixin AstNodeData!(AstType.stmt_for, 0, AstNodeState.name_register_self_done);
12 	AstNodes init_statements;
13 	AstIndex condition; // Nullable
14 	AstNodes increment_statements;
15 	AstNodes body_statements;
16 }
17 
18 void print_for(ForStmtNode* node, ref AstPrintState state)
19 {
20 	state.print("FOR");
21 	print_ast(node.init_statements, state);
22 	state.print("COND");
23 	if (node.condition)
24 		print_ast(node.condition, state);
25 	state.print("INC");
26 	print_ast(node.increment_statements, state);
27 	print_ast(node.body_statements, state);
28 }
29 
30 void post_clone_for(ForStmtNode* node, ref CloneState state)
31 {
32 	state.fixAstIndex(node.condition);
33 	state.fixAstNodes(node.init_statements);
34 	state.fixAstNodes(node.increment_statements);
35 	state.fixAstNodes(node.body_statements);
36 }
37 
38 void name_register_nested_for(ForStmtNode* node, ref NameRegisterState state) {
39 	node.state = AstNodeState.name_register_nested;
40 	require_name_register(node.init_statements, state);
41 	if (node.condition) require_name_register(node.condition, state);
42 	require_name_register(node.increment_statements, state);
43 	require_name_register(node.body_statements, state);
44 	node.state = AstNodeState.name_register_nested_done;
45 }
46 
47 void name_resolve_for(ForStmtNode* node, ref NameResolveState state) {
48 	node.state = AstNodeState.name_resolve;
49 	require_name_resolve(node.init_statements, state);
50 	if (node.condition) require_name_resolve(node.condition, state);
51 	require_name_resolve(node.increment_statements, state);
52 	require_name_resolve(node.body_statements, state);
53 	node.state = AstNodeState.name_resolve_done;
54 }
55 
56 void type_check_for(ForStmtNode* node, ref TypeCheckState state)
57 {
58 	node.state = AstNodeState.type_check;
59 	require_type_check(node.init_statements, state);
60 	if (node.condition) {
61 		require_type_check(node.condition, state);
62 		autoconvToBool(node.condition, state.context);
63 	}
64 	require_type_check(node.increment_statements, state);
65 	require_type_check(node.body_statements, state);
66 	node.state = AstNodeState.type_check_done;
67 }
68 
69 void ir_gen_for(ref IrGenState gen, IrIndex currentBlock, ref IrLabel nextStmt, ForStmtNode* n)
70 {
71 	CompilationContext* c = gen.context;
72 	// init statements
73 	IrLabel afterInitLabel = IrLabel(currentBlock);
74 	genBlock(gen, n.as!AstNode(c), n.init_statements, currentBlock, afterInitLabel);
75 	currentBlock = afterInitLabel.blockIndex;
76 
77 	// loop header
78 	IrLabel loopHeaderLabel = IrLabel(currentBlock);
79 	// increment section of body
80 	IrLabel incrementLabel = IrLabel(currentBlock);
81 
82 	// continue label
83 	IrLabel* prevLoopHeader = gen.currentLoopHeader; // save continue label
84 	gen.currentLoopHeader = &incrementLabel;
85 	scope(exit) gen.currentLoopHeader = prevLoopHeader; // restore continue label
86 
87 	// break label
88 	IrLabel* prevLoopEnd = gen.currentLoopEnd; // save break label
89 	gen.currentLoopEnd = &nextStmt;
90 	scope(exit) gen.currentLoopEnd = prevLoopEnd; // restore break label
91 
92 	gen.builder.addJumpToLabel(currentBlock, loopHeaderLabel);
93 
94 	// we need loop header in a separate block because it will
95 	// have 2 predecessors: currentBlock and loop body
96 	gen.builder.forceAllocLabelBlock(loopHeaderLabel);
97 	IrIndex loopHeaderBlock = loopHeaderLabel.blockIndex;
98 	gen.ir.getBlock(loopHeaderBlock).preventSeal = true;
99 	currentBlock = loopHeaderBlock;
100 
101 	IrLabel bodyLabel = IrLabel(currentBlock);
102 
103 	// will force allocate body block
104 	if (n.condition)
105 		ir_gen_branch(gen, n.condition, loopHeaderBlock, bodyLabel, nextStmt);
106 	else
107 		gen.builder.addJumpToLabel(loopHeaderBlock, bodyLabel);
108 
109 	// body
110 	if (bodyLabel.numPredecessors > 0)
111 	{
112 		currentBlock = bodyLabel.blockIndex;
113 		gen.builder.sealBlock(currentBlock);
114 
115 		IrBasicBlock* block = gen.ir.getBlock(currentBlock);
116 		assert(!block.isFinished);
117 		genBlock(gen, n.as!AstNode(c), n.body_statements, currentBlock, incrementLabel);
118 
119 		if (incrementLabel.numPredecessors > 0)
120 		{
121 			gen.builder.sealBlock(incrementLabel.blockIndex);
122 			genBlock(gen, n.as!AstNode(c), n.increment_statements, incrementLabel.blockIndex, loopHeaderLabel);
123 		}
124 	}
125 
126 	if (loopHeaderLabel.numPredecessors > 1)
127 		gen.ir.getBlock(loopHeaderBlock).isLoopHeader = true;
128 
129 	assert(!gen.ir.getBlock(loopHeaderBlock).isSealed);
130 	gen.builder.sealBlock(loopHeaderBlock, true);
131 }