Starting from the Bit Machine 'state', * run the machine with the TCO (off) program generated by the well-typed Simplicity expression 'dag[len]' of type 'A |- B'. * If some jet execution fails, returns 'SIMPLICITY_ERR_EXEC_JET'. * If some 'assertr' or 'assertl' combinator fails, returns 'SIMPLICITY_ERR_EXEC_ASSERT'. * Otherwise returns 'SIMPLICITY_NO_ERROR'. * * The 'state' of the Bit Machin
| 284 | * if 'dag[len]' represents a Simplicity expression with primitives then 'NULL != env'; |
| 285 | */ |
| 286 | static simplicity_err runTCO(evalState state, call* stack, const dag_node* dag, type* type_dag, size_t len, const txEnv* env) { |
| 287 | /* The program counter, 'pc', is the current combinator being interpreted. */ |
| 288 | size_t pc = len - 1; |
| 289 | |
| 290 | /* 'stack' represents the interpreter's call stack. |
| 291 | * However, the stack is not directly represented as an array. |
| 292 | * Instead, the bottom of the call stack is located at 'stack[len - 1]' and the top of the call stack is located at 'stack[pc]'. |
| 293 | * The intermediate call stack items are somewhere between 'pc' and 'len - 1'. |
| 294 | * The each call stack item references the one below it through the 'stack[i].return_to' values. |
| 295 | * The bottom of the stack's '.return_to' value is set to 'len' which is an out-of-bounds index. |
| 296 | * |
| 297 | * During CALLs, a new 'stack[i]' value is created where 'i' is index of the combinator being called, |
| 298 | * and 'stack[i].return_to' is set to the current 'pc' value. |
| 299 | * During TAIL_CALLs, 'stack[i].return_to' is instead set to the 'stack[pc].return_to' value. |
| 300 | * During RETURNs, the 'pc' is set to the 'stack[pc].return_to' value. |
| 301 | * TAIL_CALLs allows for faster returns within the interpreter (unrelated to Simplicity's TCO), |
| 302 | * skipping over intermediate combinators. |
| 303 | */ |
| 304 | stack[pc].return_to = len; |
| 305 | set_tco_flag(&stack[pc], false); |
| 306 | |
| 307 | /* 'calling' lets us know if we are entering a CALL or returning from a CALL. */ |
| 308 | bool calling = true; |
| 309 | |
| 310 | /* :TODO: Use static analysis to limit the number of iterations through this loop. */ |
| 311 | while(pc < len) { |
| 312 | stack[pc].flags |= FLAG_EXEC; |
| 313 | tag_t tag = dag[pc].tag; |
| 314 | simplicity_debug_assert(state.activeReadFrame < state.activeWriteFrame); |
| 315 | simplicity_debug_assert(state.activeReadFrame->edge <= state.activeWriteFrame->edge); |
| 316 | if (dag[pc].jet) { |
| 317 | if(!dag[pc].jet(state.activeWriteFrame, *state.activeReadFrame, env)) return SIMPLICITY_ERR_EXEC_JET; |
| 318 | /* Like IDEN and WITNESS, we want to "fallthrough" to the UNIT case. */ |
| 319 | tag = UNIT; |
| 320 | } |
| 321 | switch (tag) { |
| 322 | case COMP: |
| 323 | if (calling) { |
| 324 | /* NEW_FRAME(BITSIZE(B)) */ |
| 325 | *(state.activeWriteFrame - 1) = initWriteFrame(type_dag[COMP_B(dag, type_dag, pc)].bitSize, state.activeWriteFrame->edge); |
| 326 | state.activeWriteFrame--; |
| 327 | |
| 328 | /* CALL(dag[pc].child[0], SAME_TCO) */ |
| 329 | stack[dag[pc].child[0]].return_to = pc; |
| 330 | set_tco_flag(&stack[dag[pc].child[0]], get_tco_flag(&stack[pc])); |
| 331 | pc = dag[pc].child[0]; |
| 332 | } else { |
| 333 | /* MOVE_FRAME */ |
| 334 | simplicity_debug_assert(0 == state.activeWriteFrame->offset); |
| 335 | memmove( state.activeReadFrame->edge, state.activeWriteFrame->edge |
| 336 | , (size_t)((state.activeWriteFrame + 1)->edge - state.activeWriteFrame->edge) * sizeof(UWORD) |
| 337 | ); |
| 338 | *(state.activeReadFrame + 1) = initReadFrame(type_dag[COMP_B(dag, type_dag, pc)].bitSize, state.activeReadFrame->edge); |
| 339 | state.activeWriteFrame++; state.activeReadFrame++; |
| 340 | |
| 341 | /* TAIL_CALL(dag[pc].child[1], true) */ |
| 342 | calling = true; |
| 343 | stack[dag[pc].child[1]].return_to = stack[pc].return_to; |
no test coverage detected