| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | /* Copyright 2025 Jonathan S. Arney | ||
| 2 | * | ||
| 3 | * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 4 | * you may not use this file except in compliance with the License. | ||
| 5 | * You may obtain a copy of the License at | ||
| 6 | * | ||
| 7 | * https://github.com/jarney/gyoji/blob/master/LICENSE | ||
| 8 | * | ||
| 9 | * Unless required by applicable law or agreed to in writing, software | ||
| 10 | * distributed under the License is distributed on an "AS IS" BASIS, | ||
| 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 12 | * See the License for the specific language governing permissions and | ||
| 13 | * limitations under the License. | ||
| 14 | */ | ||
| 15 | |||
| 16 | #include <gyoji-frontend.hpp> | ||
| 17 | #include <gyoji-frontend/function-scope.hpp> | ||
| 18 | #include <sstream> | ||
| 19 | |||
| 20 | using namespace Gyoji::frontend::lowering; | ||
| 21 | |||
| 22 | /////////////////////////////////////////////////// | ||
| 23 | // Graph-theoretic utilities just to walk graphs | ||
| 24 | // and find intersections, differences. | ||
| 25 | /////////////////////////////////////////////////// | ||
| 26 | 24 | static void walk_priors( | |
| 27 | const std::map<size_t, size_t> & backward_edges, | ||
| 28 | size_t start_point, | ||
| 29 | std::map<size_t, size_t> & priors) | ||
| 30 | { | ||
| 31 | 24 | size_t p = start_point; | |
| 32 | while (p >= 0) { | ||
| 33 | 114 | priors.insert(std::pair(p,p)); | |
| 34 | 114 | const auto & it = backward_edges.find(p); | |
| 35 |
2/2✓ Branch 2 taken 24 times.
✓ Branch 3 taken 90 times.
|
114 | if (it == backward_edges.end()) { |
| 36 | 24 | break; | |
| 37 | } | ||
| 38 | 90 | p = it->second; | |
| 39 | 90 | } | |
| 40 | 24 | } | |
| 41 | |||
| 42 | static void | ||
| 43 | 12 | evaluate_scope_changes( | |
| 44 | const std::vector<const ScopeOperation*> & flat, | ||
| 45 | const std::map<size_t, size_t> & prior_to_goto, | ||
| 46 | const std::map<size_t, size_t> & prior_to_label, | ||
| 47 | std::vector<const ScopeOperation*> & skipped_initializations, | ||
| 48 | std::vector<const ScopeOperation*> & unwind_variables | ||
| 49 | ) | ||
| 50 | { | ||
| 51 |
2/2✓ Branch 5 taken 56 times.
✓ Branch 6 taken 12 times.
|
68 | for (const auto & lp : prior_to_label) { |
| 52 |
2/2✓ Branch 3 taken 20 times.
✓ Branch 4 taken 36 times.
|
56 | if (prior_to_goto.find(lp.first) == prior_to_goto.end()) { |
| 53 | 20 | const ScopeOperation *op = flat.at(lp.first); | |
| 54 |
2/2✓ Branch 1 taken 10 times.
✓ Branch 2 taken 10 times.
|
20 | if (op->get_type() == ScopeOperation::VAR_DECL) { |
| 55 | 10 | skipped_initializations.push_back(op); | |
| 56 | } | ||
| 57 | } | ||
| 58 | } | ||
| 59 |
2/2✓ Branch 5 taken 58 times.
✓ Branch 6 taken 12 times.
|
70 | for (const auto & gp : prior_to_goto) { |
| 60 |
2/2✓ Branch 3 taken 22 times.
✓ Branch 4 taken 36 times.
|
58 | if (prior_to_label.find(gp.first) == prior_to_label.end()) { |
| 61 | 22 | const ScopeOperation *op = flat.at(gp.first); | |
| 62 |
2/2✓ Branch 1 taken 12 times.
✓ Branch 2 taken 10 times.
|
22 | if (op->get_type() == ScopeOperation::VAR_DECL) { |
| 63 | 12 | unwind_variables.push_back(op); | |
| 64 | } | ||
| 65 | } | ||
| 66 | } | ||
| 67 | // We reverse the list because | ||
| 68 | // we should unwind the variables | ||
| 69 | // in the reverse order they were declared in. | ||
| 70 | 12 | std::reverse(unwind_variables.begin(), unwind_variables.end()); | |
| 71 | 12 | } | |
| 72 | |||
| 73 | /////////////////////////////////////////////////// | ||
| 74 | // ScopeOperation | ||
| 75 | /////////////////////////////////////////////////// | ||
| 76 | 954 | ScopeOperation::ScopeOperation( | |
| 77 | ScopeOperationType _type, | ||
| 78 | const Gyoji::context::SourceReference & _source_ref | ||
| 79 | 954 | ) | |
| 80 | 954 | : type(_type) | |
| 81 | 954 | , source_ref(_source_ref) | |
| 82 | 954 | {} | |
| 83 | |||
| 84 | 954 | ScopeOperation::~ScopeOperation() | |
| 85 | 954 | {} | |
| 86 | |||
| 87 | const ScopeOperation::ScopeOperationType & | ||
| 88 | 90 | ScopeOperation::get_type() const | |
| 89 | 90 | { return type; } | |
| 90 | |||
| 91 | const std::string & | ||
| 92 | 18 | ScopeOperation::get_label_name() const | |
| 93 | 18 | { return label_name; } | |
| 94 | |||
| 95 | const std::string & | ||
| 96 | 38 | ScopeOperation::get_goto_label() const | |
| 97 | 38 | { return goto_label; } | |
| 98 | |||
| 99 | const FunctionPoint & | ||
| 100 | 4 | ScopeOperation::get_goto_point() const | |
| 101 | 4 | { return *goto_point; } | |
| 102 | |||
| 103 | const std::string & | ||
| 104 | 50 | ScopeOperation::get_variable_name() const | |
| 105 | 50 | { return variable_name; } | |
| 106 | |||
| 107 | const Gyoji::mir::Type * | ||
| 108 | 8 | ScopeOperation::get_variable_type() const | |
| 109 | 8 | { return variable_type; } | |
| 110 | |||
| 111 | Gyoji::owned<ScopeOperation> | ||
| 112 | 860 | ScopeOperation::create_variable( | |
| 113 | std::string _variable_name, | ||
| 114 | const Gyoji::mir::Type *_variable_type, | ||
| 115 | const Gyoji::context::SourceReference & _source_ref | ||
| 116 | ) | ||
| 117 | { | ||
| 118 | 860 | auto op = Gyoji::owned<ScopeOperation>(new ScopeOperation(ScopeOperation::VAR_DECL, _source_ref)); | |
| 119 | 860 | op->variable_name = _variable_name; | |
| 120 | 860 | op->variable_type = _variable_type; | |
| 121 | 860 | return op; | |
| 122 | } | ||
| 123 | Gyoji::owned<ScopeOperation> | ||
| 124 | 10 | ScopeOperation::create_label( | |
| 125 | std::string _label_name, | ||
| 126 | const Gyoji::context::SourceReference & _source_ref | ||
| 127 | ) | ||
| 128 | { | ||
| 129 | 10 | auto op = Gyoji::owned<ScopeOperation>(new ScopeOperation(ScopeOperation::LABEL_DEFINITION, _source_ref)); | |
| 130 | 10 | op->label_name = _label_name; | |
| 131 | 10 | return op; | |
| 132 | } | ||
| 133 | |||
| 134 | Gyoji::owned<ScopeOperation> | ||
| 135 | 12 | ScopeOperation::create_goto( | |
| 136 | std::string _goto_label, | ||
| 137 | Gyoji::owned<FunctionPoint> _goto_point, | ||
| 138 | const Gyoji::context::SourceReference & _source_ref | ||
| 139 | ) | ||
| 140 | { | ||
| 141 | 12 | auto op = Gyoji::owned<ScopeOperation>(new ScopeOperation(ScopeOperation::GOTO_DEFINITION, _source_ref)); | |
| 142 | 12 | op->goto_label = _goto_label; | |
| 143 | 12 | op->goto_point = std::move(_goto_point); | |
| 144 | 12 | return op; | |
| 145 | } | ||
| 146 | |||
| 147 | Gyoji::owned<ScopeOperation> | ||
| 148 | 72 | ScopeOperation::create_child( | |
| 149 | Gyoji::owned<Scope> _child, | ||
| 150 | const Gyoji::context::SourceReference & _source_ref | ||
| 151 | ) | ||
| 152 | { | ||
| 153 | 72 | auto op = Gyoji::owned<ScopeOperation>(new ScopeOperation(ScopeOperation::CHILD_SCOPE, _source_ref)); | |
| 154 | 72 | op->child = std::move(_child); | |
| 155 | 72 | return op; | |
| 156 | } | ||
| 157 | |||
| 158 | const Gyoji::context::SourceReference & | ||
| 159 | 16 | ScopeOperation::get_source_ref() const | |
| 160 | 16 | { return source_ref; } | |
| 161 | |||
| 162 | /////////////////////////////////////////////////// | ||
| 163 | // Scope | ||
| 164 | /////////////////////////////////////////////////// | ||
| 165 | 344 | Scope::Scope(bool _is_unsafe) | |
| 166 | 344 | : parent(nullptr) | |
| 167 | 344 | , scope_is_loop(false) | |
| 168 | 344 | , scope_is_unsafe(_is_unsafe) | |
| 169 | 344 | , loop_break_blockid(0) | |
| 170 | 344 | , loop_continue_blockid(0) | |
| 171 | 344 | {} | |
| 172 | |||
| 173 | 4 | Scope::Scope( | |
| 174 | bool _is_unsafe, | ||
| 175 | bool _is_loop, | ||
| 176 | size_t _loop_break_blockid, | ||
| 177 | size_t _loop_continue_blockid | ||
| 178 | 4 | ) | |
| 179 | 4 | : parent(nullptr) | |
| 180 | 4 | , scope_is_loop(_is_loop) | |
| 181 | 4 | , scope_is_unsafe(_is_unsafe) | |
| 182 | 4 | , loop_break_blockid(_loop_break_blockid) | |
| 183 | 4 | , loop_continue_blockid(_loop_continue_blockid) | |
| 184 | 4 | {} | |
| 185 | |||
| 186 | 348 | Scope::~Scope() | |
| 187 | 348 | {} | |
| 188 | |||
| 189 | void | ||
| 190 | 954 | Scope::add_operation(Gyoji::owned<ScopeOperation> op) | |
| 191 | { | ||
| 192 | 954 | operations.push_back(std::move(op)); | |
| 193 | 954 | } | |
| 194 | |||
| 195 | const LocalVariable * | ||
| 196 | 3244 | Scope::get_variable(std::string name) const | |
| 197 | { | ||
| 198 | 3244 | const auto & it = variables.find(name); | |
| 199 |
2/2✓ Branch 2 taken 1178 times.
✓ Branch 3 taken 2066 times.
|
3244 | if (it == variables.end()) { |
| 200 | 1178 | return nullptr; | |
| 201 | } | ||
| 202 | 2066 | return it->second.get(); | |
| 203 | } | ||
| 204 | |||
| 205 | bool | ||
| 206 | 22 | Scope::is_loop() const | |
| 207 | { | ||
| 208 | 22 | return scope_is_loop; | |
| 209 | } | ||
| 210 | bool | ||
| 211 | 156 | Scope::is_unsafe() const | |
| 212 | { | ||
| 213 | 156 | return scope_is_unsafe; | |
| 214 | } | ||
| 215 | size_t | ||
| 216 | 2 | Scope::get_loop_break_blockid() const | |
| 217 | 2 | { return loop_break_blockid; } | |
| 218 | |||
| 219 | size_t | ||
| 220 | 2 | Scope::get_loop_continue_blockid() const | |
| 221 | 2 | { return loop_continue_blockid; } | |
| 222 | |||
| 223 | void | ||
| 224 | 860 | Scope::add_variable(std::string name, const Gyoji::mir::Type *mir_type, const Gyoji::context::SourceReference & source_ref) | |
| 225 | { | ||
| 226 | 860 | Gyoji::owned<LocalVariable> local_variable = std::make_unique<LocalVariable>(mir_type, source_ref); | |
| 227 | 860 | variables.insert(std::pair(name, std::move(local_variable))); | |
| 228 | 860 | variables_in_declaration_order.push_back(name); | |
| 229 | 860 | } | |
| 230 | |||
| 231 | const std::map<std::string, Gyoji::owned<LocalVariable>> & | ||
| 232 | ✗ | Scope::get_variables() const | |
| 233 | ✗ | { return variables; } | |
| 234 | |||
| 235 | const std::vector<std::string> & | ||
| 236 | 338 | Scope::get_variables_in_declaration_order() const | |
| 237 | 338 | { return variables_in_declaration_order; } | |
| 238 | |||
| 239 | Scope * | ||
| 240 | 1712 | Scope::get_parent() const | |
| 241 | 1712 | { return parent; } | |
| 242 | |||
| 243 | void | ||
| 244 | 72 | Scope::set_parent(Scope *_parent) | |
| 245 | 72 | { parent = _parent; } | |
| 246 | |||
| 247 | /////////////////////////////////////////////////// | ||
| 248 | // ScopeTracker | ||
| 249 | /////////////////////////////////////////////////// | ||
| 250 | 276 | ScopeTracker::ScopeTracker( | |
| 251 | bool _root_is_unsafe, | ||
| 252 | 276 | const Gyoji::context::CompilerContext & _compiler_context) | |
| 253 | 276 | : root(std::make_unique<Scope>(_root_is_unsafe)) | |
| 254 | 276 | , compiler_context(_compiler_context) | |
| 255 | 276 | , tracker_prior_point() | |
| 256 | 276 | , tracker_backward_edges() | |
| 257 | 552 | , tracker_flat() | |
| 258 | { | ||
| 259 | 276 | tracker_prior_point.push_back((size_t)-1); | |
| 260 | 276 | current = root.get(); | |
| 261 | 276 | } | |
| 262 | |||
| 263 | 276 | ScopeTracker::~ScopeTracker() | |
| 264 | { | ||
| 265 | 276 | } | |
| 266 | |||
| 267 | |||
| 268 | void | ||
| 269 | 68 | ScopeTracker::scope_push(bool _is_unsafe, const Gyoji::context::SourceReference & _source_ref) | |
| 270 | { | ||
| 271 | 68 | auto child_scope = std::make_unique<Scope>(_is_unsafe); | |
| 272 | 68 | Scope *new_current = child_scope.get(); | |
| 273 | 68 | child_scope->set_parent(current); | |
| 274 | 68 | auto child_op = ScopeOperation::create_child(std::move(child_scope), _source_ref); | |
| 275 | 68 | current->add_operation(std::move(child_op)); | |
| 276 | 68 | current = new_current; | |
| 277 | 68 | tracker_prior_point.push_back(tracker_prior_point.back()); | |
| 278 | 68 | } | |
| 279 | |||
| 280 | void | ||
| 281 | 4 | ScopeTracker::scope_push_loop(const Gyoji::context::SourceReference & _source_ref, size_t _loop_break_blockid, size_t _loop_continue_blockid) | |
| 282 | { | ||
| 283 | // Scopes associated with loops cannot directly | ||
| 284 | // be declared as unsafe, so if you want that, you should | ||
| 285 | // do it in the parent scope of the loop. | ||
| 286 | 4 | auto child_scope = std::make_unique<Scope>(false, true, _loop_break_blockid, _loop_continue_blockid); | |
| 287 | 4 | Scope *new_current = child_scope.get(); | |
| 288 | 4 | child_scope->set_parent(current); | |
| 289 | 4 | auto child_op = ScopeOperation::create_child(std::move(child_scope), _source_ref); | |
| 290 | 4 | current->add_operation(std::move(child_op)); | |
| 291 | 4 | current = new_current; | |
| 292 | 4 | tracker_prior_point.push_back(tracker_prior_point.back()); | |
| 293 | 4 | } | |
| 294 | |||
| 295 | |||
| 296 | void | ||
| 297 | 72 | ScopeTracker::scope_pop() | |
| 298 | { | ||
| 299 | 72 | current = current->get_parent(); | |
| 300 | 72 | tracker_prior_point.pop_back(); | |
| 301 | 72 | } | |
| 302 | |||
| 303 | |||
| 304 | const FunctionLabel * | ||
| 305 | 16 | ScopeTracker::get_label(std::string name) const | |
| 306 | { | ||
| 307 | 16 | const auto & it_forward_declared = labels_forward_declared.find(name); | |
| 308 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 16 times.
|
16 | if (it_forward_declared != labels_forward_declared.end()) { |
| 309 | ✗ | return it_forward_declared->second.get(); | |
| 310 | } | ||
| 311 | 16 | const auto & it = labels.find(name); | |
| 312 |
2/2✓ Branch 2 taken 14 times.
✓ Branch 3 taken 2 times.
|
16 | if (it != labels.end()) { |
| 313 | 14 | return it->second.get(); | |
| 314 | } | ||
| 315 | 2 | return nullptr; | |
| 316 | } | ||
| 317 | |||
| 318 | void | ||
| 319 | 2 | ScopeTracker::label_define( | |
| 320 | std::string label_name, | ||
| 321 | const Gyoji::context::SourceReference & _source_ref | ||
| 322 | ) | ||
| 323 | { | ||
| 324 | 2 | const auto & it_forward_declared = labels_forward_declared.find(label_name); | |
| 325 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
|
2 | if (it_forward_declared == labels_forward_declared.end()) { |
| 326 | ✗ | fprintf(stderr, "Compiler Bug! We are attempting to resolve the definition of a forward-declared label, but it wasn't forward-declared.\n"); | |
| 327 | ✗ | exit(1); | |
| 328 | return; | ||
| 329 | } | ||
| 330 | 2 | it_forward_declared->second->resolve(_source_ref); | |
| 331 | 2 | labels.insert(std::pair(label_name, std::move(it_forward_declared->second))); | |
| 332 | 2 | labels_forward_declared.erase(it_forward_declared); | |
| 333 | |||
| 334 | 2 | auto op = ScopeOperation::create_label(label_name, _source_ref); | |
| 335 | 2 | tracker_label_locations.insert(std::pair(op->get_label_name(), tracker_flat.size())); | |
| 336 | 2 | add_flat_op(op.get()); | |
| 337 | |||
| 338 | 2 | add_operation(std::move(op)); | |
| 339 | 2 | } | |
| 340 | |||
| 341 | void | ||
| 342 | 8 | ScopeTracker::label_define( | |
| 343 | std::string label_name, | ||
| 344 | size_t label_blockid, | ||
| 345 | const Gyoji::context::SourceReference & _source_ref | ||
| 346 | ) | ||
| 347 | { | ||
| 348 | 8 | Gyoji::owned<FunctionLabel> new_label = std::make_unique<FunctionLabel>(label_blockid); | |
| 349 | 8 | new_label->resolve(_source_ref); | |
| 350 | 8 | labels.insert(std::pair(label_name, std::move(new_label))); | |
| 351 | |||
| 352 | 8 | auto op = ScopeOperation::create_label(label_name, _source_ref); | |
| 353 | 8 | tracker_label_locations.insert(std::pair(op->get_label_name(), tracker_flat.size())); | |
| 354 | 8 | add_flat_op(op.get()); | |
| 355 | 8 | add_operation(std::move(op)); | |
| 356 | 8 | } | |
| 357 | |||
| 358 | // Use this for 'goto' to say we want a label, but we | ||
| 359 | // don't yet know where it will live, so put it on the | ||
| 360 | // notfound labels list. | ||
| 361 | void | ||
| 362 | 2 | ScopeTracker::label_declare(std::string label_name, size_t label_blockid) | |
| 363 | { | ||
| 364 | 2 | Gyoji::owned<FunctionLabel> new_label = std::make_unique<FunctionLabel>(label_blockid); | |
| 365 | 2 | labels_forward_declared.insert(std::pair(label_name, std::move(new_label))); | |
| 366 | 2 | } | |
| 367 | |||
| 368 | void | ||
| 369 | 882 | ScopeTracker::add_flat_op(const ScopeOperation *op) | |
| 370 | { | ||
| 371 |
2/2✓ Branch 1 taken 614 times.
✓ Branch 2 taken 268 times.
|
882 | if (tracker_prior_point.back() != (size_t)-1) { |
| 372 | 614 | tracker_backward_edges.insert(std::pair(tracker_flat.size(), tracker_prior_point.back())); | |
| 373 | } | ||
| 374 | 882 | tracker_prior_point.back() = tracker_flat.size(); | |
| 375 | 882 | tracker_flat.push_back(op); | |
| 376 | 882 | } | |
| 377 | |||
| 378 | |||
| 379 | void | ||
| 380 | 12 | ScopeTracker::add_goto( | |
| 381 | std::string goto_label, | ||
| 382 | Gyoji::owned<FunctionPoint> goto_point, | ||
| 383 | const Gyoji::context::SourceReference & _source_ref | ||
| 384 | ) | ||
| 385 | { | ||
| 386 | // TODO: | ||
| 387 | // Check if this label exists and jump to that | ||
| 388 | // label if it does. If it doesn't, forward declare it. | ||
| 389 | // and put it in the 'forward declared' labels list. | ||
| 390 | 12 | auto op = ScopeOperation::create_goto(goto_label, std::move(goto_point), _source_ref); | |
| 391 | 12 | tracker_goto_labels_at.insert(std::pair(tracker_flat.size(), op->get_goto_label())); | |
| 392 | 12 | add_flat_op(op.get()); | |
| 393 | 12 | add_operation(std::move(op)); | |
| 394 | 12 | } | |
| 395 | |||
| 396 | const LocalVariable * | ||
| 397 | 3012 | ScopeTracker::get_variable(std::string variable_name) const | |
| 398 | { | ||
| 399 | // Walk up from the current scope up to the root and | ||
| 400 | // see if this variable is defined anywhere. | ||
| 401 | 3012 | const Scope *s = current; | |
| 402 |
2/2✓ Branch 0 taken 3244 times.
✓ Branch 1 taken 946 times.
|
4190 | while (s) { |
| 403 | 3244 | const LocalVariable *existing_local_variable = s->get_variable(variable_name); | |
| 404 |
2/2✓ Branch 0 taken 2066 times.
✓ Branch 1 taken 1178 times.
|
3244 | if (existing_local_variable != nullptr) { |
| 405 | 2066 | return existing_local_variable; | |
| 406 | } | ||
| 407 | 1178 | s = s->get_parent(); | |
| 408 | } | ||
| 409 | 946 | return nullptr; | |
| 410 | } | ||
| 411 | |||
| 412 | std::vector<std::string> | ||
| 413 | 284 | ScopeTracker::get_variables_to_unwind_for_root() const | |
| 414 | { | ||
| 415 | 284 | std::vector<std::string> variables_to_unwind; | |
| 416 | 284 | const Scope *s = current; | |
| 417 |
2/2✓ Branch 0 taken 302 times.
✓ Branch 1 taken 284 times.
|
586 | while (s) { |
| 418 | // Reverse order because we want to | ||
| 419 | // call destructors in the reverse order of declaration. | ||
| 420 | 302 | auto & vars = s->get_variables_in_declaration_order(); | |
| 421 |
2/2✓ Branch 4 taken 884 times.
✓ Branch 5 taken 302 times.
|
1186 | for (std::vector<std::string>::const_reverse_iterator it = vars.rbegin(); it != vars.rend(); ++it) { |
| 422 | 884 | variables_to_unwind.push_back(*it); | |
| 423 | } | ||
| 424 | 302 | s = s->get_parent(); | |
| 425 | } | ||
| 426 | 284 | return variables_to_unwind; | |
| 427 | } | ||
| 428 | |||
| 429 | std::vector<std::string> | ||
| 430 | 36 | ScopeTracker::get_variables_to_unwind_for_scope() const | |
| 431 | { | ||
| 432 | 36 | std::vector<std::string> variables_to_unwind; | |
| 433 | 36 | const Scope *s = current; | |
| 434 | 36 | const auto & vars = s->get_variables_in_declaration_order(); | |
| 435 |
2/2✓ Branch 4 taken 12 times.
✓ Branch 5 taken 36 times.
|
48 | for (std::vector<std::string>::const_reverse_iterator it = vars.rbegin(); it != vars.rend(); ++it) { |
| 436 | 12 | variables_to_unwind.push_back(*it); | |
| 437 | } | ||
| 438 | 36 | return variables_to_unwind; | |
| 439 | } | ||
| 440 | |||
| 441 | std::vector<std::string> | ||
| 442 | 2 | ScopeTracker::get_variables_to_unwind_for_break() const | |
| 443 | { | ||
| 444 | 2 | std::vector<std::string> variables_to_unwind; | |
| 445 | 2 | const Scope *s = current; | |
| 446 |
3/6✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 2 times.
|
2 | while (s && s->is_loop()) { |
| 447 | ✗ | const auto & vars = s->get_variables_in_declaration_order(); | |
| 448 | ✗ | for (std::vector<std::string>::const_reverse_iterator it = vars.rbegin(); it != vars.rend(); ++it) { | |
| 449 | ✗ | variables_to_unwind.push_back(*it); | |
| 450 | } | ||
| 451 | ✗ | s = s->get_parent(); | |
| 452 | } | ||
| 453 | 2 | return variables_to_unwind; | |
| 454 | |||
| 455 | } | ||
| 456 | |||
| 457 | const Scope * | ||
| 458 | ✗ | ScopeTracker::get_current() const | |
| 459 | ✗ | { return current; } | |
| 460 | |||
| 461 | |||
| 462 | bool | ||
| 463 | 90 | ScopeTracker::is_unsafe() const | |
| 464 | { | ||
| 465 | 90 | Scope* cur = current; | |
| 466 |
2/2✓ Branch 0 taken 156 times.
✓ Branch 1 taken 82 times.
|
238 | while (cur) { |
| 467 |
2/2✓ Branch 1 taken 8 times.
✓ Branch 2 taken 148 times.
|
156 | if (cur->is_unsafe()) { |
| 468 | 8 | return true; | |
| 469 | } | ||
| 470 | 148 | cur = cur->get_parent(); | |
| 471 | } | ||
| 472 | 82 | return false; | |
| 473 | } | ||
| 474 | |||
| 475 | bool | ||
| 476 | 862 | ScopeTracker::add_variable(std::string variable_name, const Gyoji::mir::Type *mir_type, const Gyoji::context::SourceReference & source_ref) | |
| 477 | { | ||
| 478 | // Walk up from the current scope up to the root and | ||
| 479 | // see if this variable is defined anywhere. | ||
| 480 | 862 | const LocalVariable *maybe_existing = get_variable(variable_name); | |
| 481 | |||
| 482 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 860 times.
|
862 | if (maybe_existing != nullptr) { |
| 483 | 2 | std::unique_ptr<Gyoji::context::Error> error = std::make_unique<Gyoji::context::Error>("Duplicate Local Variable."); | |
| 484 | 4 | error->add_message(source_ref, | |
| 485 | 4 | std::string("Variable with name ") + variable_name + " is already in scope and cannot be duplicated."); | |
| 486 | 4 | error->add_message(maybe_existing->get_source_ref(), | |
| 487 | "First declared here."); | ||
| 488 | |||
| 489 | 2 | compiler_context | |
| 490 | 2 | .get_errors() | |
| 491 | 2 | .add_error(std::move(error)); | |
| 492 | 2 | return false; | |
| 493 | 2 | } | |
| 494 | |||
| 495 | |||
| 496 | // Variable was not declared earlier, so we add it to | ||
| 497 | // the current scope. | ||
| 498 | 860 | current->add_variable(variable_name, mir_type, source_ref); | |
| 499 | |||
| 500 | 860 | auto local_var_op = ScopeOperation::create_variable(variable_name, mir_type, source_ref); | |
| 501 | 860 | add_flat_op(local_var_op.get()); | |
| 502 | 860 | add_operation(std::move(local_var_op)); | |
| 503 | |||
| 504 | 860 | return true; | |
| 505 | 860 | } | |
| 506 | |||
| 507 | |||
| 508 | // TODO: We should give a better interface | ||
| 509 | // here so we can easily declare locals, labels, gotos. | ||
| 510 | void | ||
| 511 | 882 | ScopeTracker::add_operation(Gyoji::owned<ScopeOperation> op) | |
| 512 | { | ||
| 513 | 882 | current->add_operation(std::move(op)); | |
| 514 | 882 | } | |
| 515 | |||
| 516 | |||
| 517 | // Now from this, I can evaluate | ||
| 518 | // what's legal. We only need to add the 'SourceReference' | ||
| 519 | // stuff in order to produce good errors for this. | ||
| 520 | void | ||
| 521 | 6 | ScopeTracker::dump() const | |
| 522 | { | ||
| 523 | 6 | Scope *s = root.get(); | |
| 524 | 6 | fprintf(stderr, "{\n"); | |
| 525 | 6 | s->dump(0); | |
| 526 | 6 | fprintf(stderr, "}\n"); | |
| 527 |
2/2✓ Branch 1 taken 48 times.
✓ Branch 2 taken 6 times.
|
54 | for (size_t i = 0; i < tracker_flat.size(); i++) { |
| 528 | 48 | const auto & op = tracker_flat.at(i); | |
| 529 |
3/5✓ Branch 1 taken 30 times.
✓ Branch 2 taken 8 times.
✓ Branch 3 taken 10 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
|
48 | switch (op->get_type()) { |
| 530 | 30 | case ScopeOperation::VAR_DECL: | |
| 531 | 30 | fprintf(stderr, "%ld Var decl %s\n", i, op->get_variable_name().c_str()); | |
| 532 | 30 | break; | |
| 533 | 8 | case ScopeOperation::LABEL_DEFINITION: | |
| 534 | 8 | fprintf(stderr, "%ld label %s\n", i, op->get_label_name().c_str()); | |
| 535 | 8 | break; | |
| 536 | 10 | case ScopeOperation::GOTO_DEFINITION: | |
| 537 | 10 | fprintf(stderr, "%ld goto %s\n", i, op->get_goto_label().c_str()); | |
| 538 | 10 | break; | |
| 539 | ✗ | case ScopeOperation::CHILD_SCOPE: | |
| 540 | ✗ | fprintf(stderr, "This should not be here\n"); | |
| 541 | ✗ | exit(1); | |
| 542 | } | ||
| 543 | } | ||
| 544 | 6 | } | |
| 545 | |||
| 546 | bool | ||
| 547 | 4 | ScopeTracker::is_in_loop() const | |
| 548 | { | ||
| 549 | 4 | const Scope *s = current; | |
| 550 |
1/2✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
|
10 | while (s) { |
| 551 |
2/2✓ Branch 1 taken 4 times.
✓ Branch 2 taken 6 times.
|
10 | if (s->is_loop()) { |
| 552 | 4 | return true; | |
| 553 | } | ||
| 554 | 6 | s = s->get_parent(); | |
| 555 | } | ||
| 556 | ✗ | return false; | |
| 557 | } | ||
| 558 | |||
| 559 | size_t | ||
| 560 | 2 | ScopeTracker::get_loop_break_blockid() const | |
| 561 | { | ||
| 562 | 2 | const Scope *s = current; | |
| 563 |
1/2✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
|
4 | while (s) { |
| 564 |
2/2✓ Branch 1 taken 2 times.
✓ Branch 2 taken 2 times.
|
4 | if (s->is_loop()) { |
| 565 | 2 | return s->get_loop_break_blockid(); | |
| 566 | } | ||
| 567 | 2 | s = s->get_parent(); | |
| 568 | } | ||
| 569 | ✗ | return 0; | |
| 570 | } | ||
| 571 | |||
| 572 | size_t | ||
| 573 | 2 | ScopeTracker::get_loop_continue_blockid() const | |
| 574 | { | ||
| 575 | 2 | const Scope *s = current; | |
| 576 |
1/2✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
|
6 | while (s) { |
| 577 |
2/2✓ Branch 1 taken 2 times.
✓ Branch 2 taken 4 times.
|
6 | if (s->is_loop()) { |
| 578 | 2 | return s->get_loop_continue_blockid(); | |
| 579 | } | ||
| 580 | 4 | s = s->get_parent(); | |
| 581 | } | ||
| 582 | ✗ | return 0; | |
| 583 | } | ||
| 584 | |||
| 585 | bool | ||
| 586 | 276 | ScopeTracker::check( | |
| 587 | std::vector<std::pair<const ScopeOperation*, std::vector<const ScopeOperation*>>> & goto_fixups | ||
| 588 | ) const | ||
| 589 | { | ||
| 590 | 276 | bool ok = true; | |
| 591 |
2/2✓ Branch 5 taken 12 times.
✓ Branch 6 taken 276 times.
|
288 | for (const auto & goto_point : tracker_goto_labels_at) { |
| 592 | 12 | const ScopeOperation *goto_operation = tracker_flat.at(goto_point.first); | |
| 593 | 12 | const FunctionLabel *function_label = get_label(goto_operation->get_goto_label()); | |
| 594 |
3/6✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 12 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 12 times.
|
12 | if (function_label == nullptr || !function_label->is_resolved()) { |
| 595 | ✗ | std::unique_ptr<Gyoji::context::Error> error = std::make_unique<Gyoji::context::Error>("Goto for an un-defined label."); | |
| 596 | ✗ | error->add_message(goto_operation->get_source_ref(), | |
| 597 | ✗ | std::string("Goto label ") + goto_operation->get_goto_label() + " had an undefined destination."); | |
| 598 | ✗ | compiler_context | |
| 599 | ✗ | .get_errors() | |
| 600 | ✗ | .add_error(std::move(error)); | |
| 601 | ✗ | ok = false; | |
| 602 | ✗ | continue; | |
| 603 | ✗ | } | |
| 604 | |||
| 605 | 12 | std::map<size_t, size_t> prior_to_goto; | |
| 606 | 12 | walk_priors(tracker_backward_edges, goto_point.first, prior_to_goto); | |
| 607 | |||
| 608 | 12 | std::map<size_t, size_t> prior_to_label; | |
| 609 | 12 | const auto it = tracker_label_locations.find(goto_point.second); | |
| 610 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 12 times.
|
12 | if (it == tracker_label_locations.end()) { |
| 611 | ✗ | fprintf(stderr, "This is a bug, we have a defined label, but no location for it\n"); | |
| 612 | ✗ | exit(1); | |
| 613 | } | ||
| 614 | 12 | size_t label_point = it->second; | |
| 615 | |||
| 616 | 12 | walk_priors(tracker_backward_edges, label_point, prior_to_label); | |
| 617 | |||
| 618 | 12 | std::vector<const ScopeOperation*> skipped_initializations; | |
| 619 | 12 | std::vector<const ScopeOperation*> unwind_variables; | |
| 620 | |||
| 621 | 12 | evaluate_scope_changes(tracker_flat, prior_to_goto, prior_to_label, skipped_initializations, unwind_variables); | |
| 622 | |||
| 623 |
2/2✓ Branch 1 taken 4 times.
✓ Branch 2 taken 8 times.
|
12 | if (skipped_initializations.size() > 0) { |
| 624 | 4 | std::unique_ptr<Gyoji::context::Error> error = std::make_unique<Gyoji::context::Error>("Goto would skip initialization."); | |
| 625 | 8 | error->add_message(goto_operation->get_source_ref(), | |
| 626 | 16 | std::string("Goto label ") + goto_operation->get_goto_label() + std::string(" would skip initialization of variables in destination scope.")); | |
| 627 | 8 | error->add_message(function_label->get_source_ref(), | |
| 628 | "Label declared here."); | ||
| 629 | 8 | error->add_message(skipped_initializations.at(0)->get_source_ref(), | |
| 630 | "Skipped initialization occurs here."); | ||
| 631 | 4 | compiler_context | |
| 632 | 4 | .get_errors() | |
| 633 | 4 | .add_error(std::move(error)); | |
| 634 | 4 | ok = false; | |
| 635 | 4 | } | |
| 636 | |||
| 637 | 12 | goto_fixups.push_back(std::pair(goto_operation, unwind_variables)); | |
| 638 |
2/2✓ Branch 5 taken 12 times.
✓ Branch 6 taken 12 times.
|
24 | for (const auto & s : unwind_variables) { |
| 639 | 12 | fprintf(stderr, "Unwind %s\n", s->get_variable_name().c_str()); | |
| 640 | } | ||
| 641 | 12 | } | |
| 642 | 276 | return ok; | |
| 643 | } | ||
| 644 | |||
| 645 | void | ||
| 646 | 64 | ScopeOperation::dump(int indent) const | |
| 647 | { | ||
| 648 | 64 | std::string pad(indent*8, ' '); | |
| 649 | |||
| 650 |
4/5✓ Branch 0 taken 30 times.
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 10 times.
✓ Branch 3 taken 16 times.
✗ Branch 4 not taken.
|
64 | switch (type) { |
| 651 | 30 | case ScopeOperation::VAR_DECL: | |
| 652 | { | ||
| 653 | 60 | std::string var = pad + std::string("var ") + variable_name; | |
| 654 | 30 | fprintf(stderr, "%s\n", var.c_str()); | |
| 655 | 30 | } | |
| 656 | 30 | break; | |
| 657 | 8 | case ScopeOperation::LABEL_DEFINITION: | |
| 658 | { | ||
| 659 | 16 | std::string var = pad + std::string("label ") + label_name; | |
| 660 | 8 | fprintf(stderr, "%s\n", var.c_str()); | |
| 661 | 8 | } | |
| 662 | 8 | break; | |
| 663 | 10 | case ScopeOperation::GOTO_DEFINITION: | |
| 664 | { | ||
| 665 | 20 | std::string var = pad + std::string("goto ") + goto_label; | |
| 666 | 10 | fprintf(stderr, "%s\n", var.c_str()); | |
| 667 | 10 | } | |
| 668 | 10 | break; | |
| 669 | 16 | case ScopeOperation::CHILD_SCOPE: | |
| 670 | { | ||
| 671 | 16 | std::string open = pad + "{"; | |
| 672 | 16 | fprintf(stderr, "%s\n", open.c_str()); | |
| 673 | 16 | child->dump(indent+1); | |
| 674 | 16 | std::string close = pad + "}"; | |
| 675 | 16 | fprintf(stderr, "%s\n", close.c_str()); | |
| 676 | 16 | } | |
| 677 | 16 | break; | |
| 678 | } | ||
| 679 | 64 | } | |
| 680 | |||
| 681 | void | ||
| 682 | 22 | Scope::dump(int indent) const | |
| 683 | { | ||
| 684 | 22 | std::string pad(indent*8, ' '); | |
| 685 |
2/2✓ Branch 5 taken 64 times.
✓ Branch 6 taken 22 times.
|
86 | for (const auto & op : operations) { |
| 686 | 64 | op->dump(indent+1); | |
| 687 | } | ||
| 688 | 22 | } | |
| 689 | |||
| 690 | /////////////////////////////////////////// | ||
| 691 | 860 | LocalVariable::LocalVariable( | |
| 692 | const Gyoji::mir::Type *_type, | ||
| 693 | const Gyoji::context::SourceReference & _source_ref | ||
| 694 | 860 | ) | |
| 695 | 860 | : type(_type) | |
| 696 | 860 | , source_ref(_source_ref) | |
| 697 | 860 | {} | |
| 698 | |||
| 699 | 860 | LocalVariable::~LocalVariable() | |
| 700 | 860 | {} | |
| 701 | |||
| 702 | const Gyoji::mir::Type * | ||
| 703 | 3232 | LocalVariable::get_type() const | |
| 704 | 3232 | { return type; } | |
| 705 | |||
| 706 | const Gyoji::context::SourceReference & | ||
| 707 | 2 | LocalVariable::get_source_ref() const | |
| 708 | 2 | { return source_ref; } | |
| 709 | |||
| 710 | ///////////////////////////////////// | ||
| 711 | // FunctionLabel | ||
| 712 | ///////////////////////////////////// | ||
| 713 | 10 | FunctionLabel::FunctionLabel( | |
| 714 | size_t _block_id | ||
| 715 | 10 | ) | |
| 716 | 10 | : resolved(false) | |
| 717 | 10 | , block_id(_block_id) | |
| 718 | 10 | , src_ref(nullptr) | |
| 719 | 10 | {} | |
| 720 | 10 | FunctionLabel::~FunctionLabel() | |
| 721 | 10 | {} | |
| 722 | |||
| 723 | const Gyoji::context::SourceReference & | ||
| 724 | 4 | FunctionLabel::get_source_ref() const | |
| 725 | 4 | { return *src_ref; } | |
| 726 | |||
| 727 | size_t | ||
| 728 | 2 | FunctionLabel::get_block() const | |
| 729 | 2 | { return block_id; } | |
| 730 | |||
| 731 | bool | ||
| 732 | 12 | FunctionLabel::is_resolved() const | |
| 733 | 12 | { return resolved; } | |
| 734 | |||
| 735 | void | ||
| 736 | 10 | FunctionLabel::resolve(const Gyoji::context::SourceReference & _src_ref) | |
| 737 | { | ||
| 738 | 10 | resolved = true; | |
| 739 | 10 | src_ref = &_src_ref; | |
| 740 | 10 | } | |
| 741 | |||
| 742 | //////////////////////////////////////////////// | ||
| 743 | // FunctionPoint | ||
| 744 | //////////////////////////////////////////////// | ||
| 745 | 12 | FunctionPoint::FunctionPoint(size_t _basic_block_id, size_t _location) | |
| 746 | 12 | : basic_block_id(_basic_block_id) | |
| 747 | 12 | , location(_location) | |
| 748 | 12 | {} | |
| 749 | 12 | FunctionPoint::~FunctionPoint() | |
| 750 | 12 | {} | |
| 751 | |||
| 752 | size_t | ||
| 753 | 2 | FunctionPoint::get_basic_block_id() const | |
| 754 | 2 | { return basic_block_id; } | |
| 755 | |||
| 756 | size_t | ||
| 757 | 2 | FunctionPoint::get_location() const | |
| 758 | 2 | { return location; } | |
| 759 |