1 //: Jump primitives
  2 
  3 :(scenario jump_can_skip_instructions)
  4 def main [
  5   jump 1:offset
  6   1:num <- copy 1
  7 ]
  8 +run: jump {1: "offset"}
  9 -run: {1: "number"} <- copy {1: "literal"}
 10 -mem: storing 1 in location 1
 11 
 12 :(before "End Primitive Recipe Declarations")
 13 JUMP,
 14 :(before "End Primitive Recipe Numbers")
 15 put(Recipe_ordinal, "jump", JUMP);
 16 :(before "End Primitive Recipe Checks")
 17 case JUMP: {
 18   if (SIZE(inst.ingredients) != 1) {
 19     raise << maybe(get(Recipe, r).name) << "'" << to_original_string(inst) << "' should get exactly one ingredient\n" << end();
 20     break;
 21   }
 22   if (!is_literal(inst.ingredients.at(0))) {
 23     raise << maybe(get(Recipe, r).name) << "first ingredient of '" << to_original_string(inst) << "' should be a label or offset, but '" << inst.ingredients.at(0).name << "' has type '" << names_to_string_without_quotes(inst.ingredients.at(0).type) << "'\n" << end();
 24     break;
 25   }
 26   if (!inst.products.empty()) {
 27     raise << maybe(get(Recipe, r).name) << "'jump' instructions write no products\n" << end();
 28     break;
 29   }
 30   break;
 31 }
 32 :(before "End Primitive Recipe Implementations")
 33 case JUMP: {
 34   assert(current_instruction().ingredients.at(0).initialized);
 35   current_step_index() += ingredients.at(0).at(0)+1;
 36   trace(9998, "run") << "jumping to instruction " << current_step_index() << end();
 37   // skip rest of this instruction
 38   write_products = false;
 39   fall_through_to_next_instruction = false;
 40   break;
 41 }
 42 
 43 //: special type to designate jump targets
 44 :(before "End Mu Types Initialization")
 45 put(Type_ordinal, "offset", 0);
 46 
 47 :(scenario jump_backward)
 48 def main [
 49   jump 1:offset  # 0 -+
 50   jump 3:offset  #    |   +-+ 1
 51                  #   \/  /\ |
 52   jump -2:offset #  2 +-->+ |
 53 ]                #         \/ 3
 54 +run: jump {1: "offset"}
 55 +run: jump {-2: "offset"}
 56 +run: jump {3: "offset"}
 57 
 58 :(scenario jump_takes_no_products)
 59 % Hide_errors = true;
 60 def main [
 61   1:num <- jump 1
 62 ]
 63 +error: main: 'jump' instructions write no products
 64 
 65 :(before "End Primitive Recipe Declarations")
 66 JUMP_IF,
 67 :(before "End Primitive Recipe Numbers")
 68 put(Recipe_ordinal, "jump-if", JUMP_IF);
 69 :(before "End Primitive Recipe Checks")
 70 case JUMP_IF: {
 71   if (SIZE(inst.ingredients) != 2) {
 72     raise << maybe(get(Recipe, r).name) << "'" << to_original_string(inst) << "' should get exactly two ingredients\n" << end();
 73     break;
 74   }
 75   if (!is_mu_address(inst.ingredients.at(0)) && !is_mu_scalar(inst.ingredients.at(0))) {
 76     raise << maybe(get(Recipe, r).name) << "'" << to_original_string(inst) << "' requires a boolean for its first ingredient, but '" << inst.ingredients.at(0).name << "' has type '" << names_to_string_without_quotes(inst.ingredients.at(0).type) << "'\n" << end();
 77     break;
 78   }
 79   if (!is_literal(inst.ingredients.at(1))) {
 80     raise << maybe(get(Recipe, r).name) << "'" << to_original_string(inst) << "' requires a label or offset for its second ingredient, but '" << inst.ingredients.at(1).name << "' has type '" << names_to_string_without_quotes(inst.ingredients.at(1).type) << "'\n" << end();
 81     break;
 82   }
 83   if (!inst.products.empty()) {
 84     raise << maybe(get(Recipe, r).name) << "'jump-if' instructions write no products\n" << end();
 85     break;
 86   }
 87   // End JUMP_IF Checks
 88   break;
 89 }
 90 :(before "End Primitive Recipe Implementations")
 91 case JUMP_IF: {
 92   assert(current_instruction().ingredients.at(1).initialized);
 93   if (!scalar_ingredient(ingredients, 0)) {
 94     trace(9998, "run") << "jump-if fell through" << end();
 95     break;
 96   }
 97   current_step_index() += ingredients.at(1).at(0)+1;
 98   trace(9998, "run") << "jumping to instruction " << current_step_index() << end();
 99   // skip rest of this instruction
100   write_products = false;
101   fall_through_to_next_instruction = false;
102   break;
103 }
104 
105 :(scenario jump_if)
106 def main [
107   jump-if 999, 1:offset
108   123:num <- copy 1
109 ]
110 +run: jump-if {999: "literal"}, {1: "offset"}
111 +run: jumping to instruction 2
112 -run: {123: "number"} <- copy {1: "literal"}
113 -mem: storing 1 in location 123
114 
115 :(scenario jump_if_fallthrough)
116 def main [
117   jump-if 0, 1:offset
118   123:num <- copy 1
119 ]
120 +run: jump-if {0: "literal"}, {1: "offset"}
121 +run: jump-if fell through
122 +run: {123: "number"} <- copy {1: "literal"}
123 +mem: storing 1 in location 123
124 
125 :(scenario jump_if_on_address)
126 def main [
127   10:num/alloc-id, 11:num <- copy 0, 999
128   jump-if 10:&:number, 1:offset
129   123:num <- copy 1
130 ]
131 +run: jump-if {10: ("address" "number")}, {1: "offset"}
132 +run: jumping to instruction 3
133 -run: {123: "number"} <- copy {1: "literal"}
134 -mem: storing 1 in location 123
135 
136 :(before "End Primitive Recipe Declarations")
137 JUMP_UNLESS,
138 :(before "End Primitive Recipe Numbers")
139 put(Recipe_ordinal, "jump-unless", JUMP_UNLESS);
140 :(before "End Primitive Recipe Checks")
141 case JUMP_UNLESS: {
142   if (SIZE(inst.ingredients) != 2) {
143     raise << maybe(get(Recipe, r).name) << "'" << to_original_string(inst) << "' should get exactly two ingredients\n" << end();
144     break;
145   }
146   if (!is_mu_address(inst.ingredients.at(0)) && !is_mu_scalar(inst.ingredients.at(0))) {
147     raise << maybe(get(Recipe, r).name) << "'" << to_original_string(inst) << "' requires a boolean for its first ingredient, but '" << inst.ingredients.at(0).name << "' has type '" << names_to_string_without_quotes(inst.ingredients.at(0).type) << "'\n" << end();
148     break;
149   }
150   if (!is_literal(inst.ingredients.at(1))) {
151     raise << maybe(get(Recipe, r).name) << "'" << to_original_string(inst) << "' requires a label or offset for its second ingredient, but '" << inst.ingredients.at(1).name << "' has type '" << names_to_string_without_quotes(inst.ingredients.at(1).type) << "'\n" << end();
152     break;
153   }
154   if (!inst.products.empty()) {
155     raise << maybe(get(Recipe, r).name) << "'jump' instructions write no products\n" << end();
156     break;
157   }
158   // End JUMP_UNLESS Checks
159   break;
160 }
161 :(before "End Primitive Recipe Implementations")
162 case JUMP_UNLESS: {
163   assert(current_instruction().ingredients.at(1).initialized);
164   if (scalar_ingredient(ingredients, 0)) {
165     trace(9998, "run") << "jump-unless fell through" << end();
166     break;
167   }
168   current_step_index() += ingredients.at(1).at(0)+1;
169   trace(9998, "run") << "jumping to instruction " << current_step_index() << end();
170   // skip rest of this instruction
171   write_products = false;
172   fall_through_to_next_instruction = false;
173   break;
174 }
175 
176 :(scenario jump_unless)
177 def main [
178   jump-unless 0, 1:offset
179   123:num <- copy 1
180 ]
181 +run: jump-unless {0: "literal"}, {1: "offset"}
182 +run: jumping to instruction 2
183 -run: {123: "number"} <- copy {1: "literal"}
184 -mem: storing 1 in location 123
185 
186 :(scenario jump_unless_fallthrough)
187 def main [
188   jump-unless 999, 1:offset
189   123:num <- copy 1
190 ]
191 +run: jump-unless {999: "literal"}, {1: "offset"}
192 +run: jump-unless fell through
193 +run: {123: "number"} <- copy {1: "literal"}
194 +mem: storing 1 in location 123