void modify_branch_so_that_state_access_is_indexed(Branch* branch, int index) { Term* stateInput = find_state_input(branch); if (stateInput == NULL) return; // If the state output is connected directly to state input, then do nothing. Term* stateOutput = find_state_output(branch); if (stateOutput->input(0) == stateInput) return; Term* unpackList = apply(branch, FUNCS.unpack_state_from_list, TermList(stateInput)); unpackList->setIntProp("index", index); move_after_inputs(unpackList); for (int i=0; i < stateInput->users.length(); i++) { Term* term = stateInput->users[i]; if (term == unpackList) continue; remap_pointers_quick(term, stateInput, unpackList); } Term* stateResult = stateOutput->input(0); ca_assert(stateResult != NULL); Term* packList = apply(branch, FUNCS.pack_state_to_list, TermList(stateInput, stateResult)); packList->setIntProp("index", index); packList->setBoolProp("final", true); set_input(stateOutput, 0, packList); move_after(packList, stateResult); }
void block_update_state_type(Block* block) { if (!block_state_type_is_out_of_date(block)) return; // Recreate the state type Type* type = create_compound_type(); // TODO: give this new type a nice name for (int i=0; i < block->length(); i++) { Term* term = block->get(i); if (term == NULL) continue; if (term->function != FUNCS.unpack_state || FUNCS.unpack_state == NULL) continue; Term* identifyingTerm = term->input(1); caValue* fieldName = get_unique_name(identifyingTerm); ca_assert(is_string(fieldName)); ca_assert(!string_eq(fieldName, "")); compound_type_append_field(type, declared_type(term), as_cstring(fieldName)); } block->stateType = type; block_remove_property(block, sym_DirtyStateType); // Might need to update any existing pack_state calls. block_update_pack_state_calls(block); }
Term* loop_find_condition(Block* block) { Term* conditionCheck = loop_find_condition_check(block); if (conditionCheck != NULL) return conditionCheck->input(0); return NULL; }
void BlockInputIterator::advanceWhileInvalid() { possibly_invalid: if (finished()) return; Term* current = blockIterator.current(); if (current == NULL) { blockIterator.advance(); inputIndex = 0; goto possibly_invalid; } if (inputIndex >= current->numInputs()) { blockIterator.advance(); inputIndex = 0; goto possibly_invalid; } if (current->input(inputIndex) == NULL) { inputIndex++; goto possibly_invalid; } }
void bug_reproducing_list_after_eval() { // There once was a bug where source repro would fail when using a list // in a vectorized function, but only after that piece of code had been // evaluated. This was because vectorize_vv was calling apply() which // was changing the 'statement' property of its inputs. Branch branch; Term* sum = branch.compile("[1 1] + [1 1]"); Term* in0 = sum->input(0); Term* in1 = sum->input(0); test_equals(get_term_source_text(in0), "[1 1]"); test_equals(get_term_source_text(in1), "[1 1]"); test_equals(get_input_source_text(sum, 0), "[1 1] "); test_equals(get_input_source_text(sum, 1), " [1 1]"); test_equals(get_branch_source_text(&branch), "[1 1] + [1 1]"); evaluate_branch(&branch); test_equals(get_term_source_text(in0), "[1 1]"); test_equals(get_term_source_text(in1), "[1 1]"); test_equals(get_input_source_text(sum, 0), "[1 1] "); test_equals(get_input_source_text(sum, 1), " [1 1]"); test_equals(get_branch_source_text(&branch), "[1 1] + [1 1]"); }
bool term_is_observable_after(Term* term, Term* location) { // Check if "term" can be observed after the "location". // // Typically, "location" uses "term" as an input, and we're trying to decide // whether the call at "location" can move/consume the value. if (term_is_observable_for_special_reasons(term)) return true; for (int i=0; i < user_count(term); i++) { Term* user = term_user(term, i); if (user->function == FUNCS.upvalue) return true; if (terms_are_in_different_switch_conditions(location, user)) continue; if (term_accesses_input_from_inside_loop(user, term)) { bool userOnlyAccessesOnFirstIteration = is_input_placeholder(user) && user->numInputs() == 2 && user->input(1) != term; if (!userOnlyAccessesOnFirstIteration) return true; } if (is_located_after(user, location)) return true; } return false; }
void branch_update_state_type(Branch* branch) { if (branch_state_type_is_out_of_date(branch)) { // TODO: Handle the case where the stateType should go from non-NULL to NULL // Recreate the state type Type* type = create_compound_type(); // TODO: give this new type a nice name for (int i=0; i < branch->length(); i++) { Term* term = branch->get(i); if (term == NULL) continue; if (term->function != FUNCS.unpack_state || FUNCS.unpack_state == NULL) continue; Term* identifyingTerm = term->input(1); compound_type_append_field(type, declared_type(term), unique_name(identifyingTerm)); } branch->stateType = type; // Might need to update any existing pack_state calls. branch_update_existing_pack_state_calls(branch); } }
Term* if_block_append_case(Term* ifBlock, Term* input) { Branch* contents = nested_contents(ifBlock); int insertPos = 0; for (int i=0; i < contents->length(); i++) { Term* term = contents->get(i); if (term->function == FUNCS.input) insertPos = term->index + 1; // Insert position is right after the last non-default case. if (term->function == FUNCS.case_func && term->input(0) != NULL) insertPos = term->index + 1; } Term* newCase = apply(contents, FUNCS.case_func, TermList(input)); contents->move(newCase, insertPos); // Add existing input placeholders to this case for (int i=0;; i++) { Term* placeholder = get_input_placeholder(contents, i); if (placeholder == NULL) break; Term* localPlaceholder = append_input_placeholder(nested_contents(newCase)); change_declared_type(localPlaceholder, placeholder->type); } return newCase; }
void repoint_terms_to_use_input_placeholders(Branch* contents) { // Visit every term for (int i=0; i < contents->length(); i++) { Term* term = contents->get(i); // Visit every input for (int inputIndex=0; inputIndex < term->numInputs(); inputIndex++) { Term* input = term->input(inputIndex); if (input == NULL) continue; // If the input is outside this branch, then see if we have a named // input that could be used instead. if (input->owningBranch == contents || input->name == "") continue; Term* replacement = find_input_placeholder_with_name(contents, input->name.c_str()); if (replacement == NULL) continue; set_input(term, inputIndex, replacement); } } }
void tv_static_type_query(Type* type, StaticTypeQuery* query) { Term* subject = query->subject; Type* subjectType = query->subjectType; // If the type doesn't have a specific size, then accept any list. if (!list_type_has_specific_size(&type->parameter)) { if (is_list_based_type(subjectType)) return query->succeed(); else return query->fail(); } caValue* expectedElementTypes = list_get_type_list_from_type(type); // Special case when looking at a call to list(); look at inputs instead of // looking at the result. if (subject && subject->function == FUNCS.list) { if (subject->numInputs() != circa_count(expectedElementTypes)) return query->fail(); for (int i=0; i < circa_count(expectedElementTypes); i++) if (!circa::term_output_always_satisfies_type( subject->input(i), as_type(circa_index(expectedElementTypes, i)))) return query->fail(); return query->succeed(); } // Look at the subject's type. if (!list_type_has_specific_size(&subjectType->parameter)) return query->fail(); caValue* subjectElementTypes = list_get_type_list_from_type(subjectType); bool anyUnableToDetermine = false; for (int i=0; i < circa_count(expectedElementTypes); i++) { if (i >= circa_count(subjectElementTypes)) return query->fail(); StaticTypeQuery::Result result = run_static_type_query( as_type(circa_index(expectedElementTypes,i)), as_type(circa_index(subjectElementTypes,i))); // If any of these fail, then fail. if (result == StaticTypeQuery::FAIL) return query->fail(); if (result == StaticTypeQuery::UNABLE_TO_DETERMINE) anyUnableToDetermine = true; } if (anyUnableToDetermine) return query->unableToDetermine(); else return query->succeed(); }
void Term__input(VM* vm) { Term* t = as_term_ref(vm->input(0)); if (t == NULL) return vm->throw_str("NULL term"); int index = vm->input(1)->as_i(); if (index >= t->numInputs()) set_term_ref(vm->output(), NULL); else set_term_ref(vm->output(), t->input(index)); }
void Term__inputs(VM* vm) { Term* t = as_term_ref(vm->input(0)); if (t == NULL) return vm->throw_str("NULL term"); Value* output = vm->output(); circa_set_list(output, t->numInputs()); for (int i=0; i < t->numInputs(); i++) set_term_ref(circa_index(output, i), t->input(i)); }
void Term__inputs(caStack* stack) { Term* t = as_term_ref(circa_input(stack, 0)); if (t == NULL) return circa_output_error(stack, "NULL reference"); caValue* output = circa_output(stack, 0); circa_set_list(output, t->numInputs()); for (int i=0; i < t->numInputs(); i++) set_term_ref(circa_index(output, i), t->input(i)); }
Term* statically_infer_length_func(Block* block, Term* term) { Term* input = term->input(0); if (input->function == FUNCS.copy) return statically_infer_length_func(block, input->input(0)); if (input->function == FUNCS.list) return create_int(block, input->numInputs()); if (input->function == FUNCS.list_append) { Term* leftLength = apply(block, FUNCS.length, TermList(input->input(0))); return apply(block, FUNCS.add, TermList( statically_infer_length_func(block, leftLength), create_int(block, 1))); } // Give up std::cout << "statically_infer_length_func didn't understand: " << input->function->name << std::endl; return create_string(block, "unknown"); }
Term* statically_infer_length_func(Branch* branch, Term* term) { Term* input = term->input(0); if (input->function == FUNCS.copy) return statically_infer_length_func(branch, input->input(0)); if (input->function == FUNCS.list) return create_int(branch, input->numInputs()); if (input->function == LIST_APPEND_FUNC) { Term* leftLength = apply(branch, FUNCS.length, TermList(input->input(0))); return apply(branch, FUNCS.add, TermList( statically_infer_length_func(branch, leftLength), create_int(branch, 1))); } // Give up std::cout << "statically_infer_length_func didn't understand: " << input->function->name << std::endl; return create_symbol_value(branch, name_Unknown); }
void Term__input(caStack* stack) { Term* t = as_term_ref(circa_input(stack, 0)); if (t == NULL) { circa_output_error(stack, "NULL reference"); return; } int index = circa_int_input(stack, 1); if (index >= t->numInputs()) set_term_ref(circa_output(stack, 0), NULL); else set_term_ref(circa_output(stack, 0), t->input(index)); }
Term* loop_find_iterator_advance(Block* block) { Term* iterator = loop_find_iterator(block); for (int i=0; i < block->length(); i++) { Term* term = block->get(i); if (term->input(0) != iterator) continue; Value* methodName = term->getProp(s_method_name); if (methodName && string_equals(methodName, "advance")) return term; } return NULL; }
void Term__trace_dependents(VM* vm) { Term* term = as_term_ref(vm->input(0)); Block* untilBlock = as_block(vm->input(1)); Value* out = vm->output(); set_list(out, 0); std::set<Term*> included; UpwardIterator upwardIterator(term); upwardIterator.stopAt(untilBlock); // Look at starting term, because UpwardIterator doesn't yield starting term. // TODO: Should fix UpwardIterator for (int i=0; i < term->numInputs(); i++) { Term* input = term->input(i); if (input != NULL) { set_term_ref(list_append(out), input); included.insert(input); } } for (; upwardIterator.unfinished(); upwardIterator.advance()) { Term* current = upwardIterator.current(); if (current == term || included.find(current) != included.end()) { for (int i=0; i < current->numInputs(); i++) { Term* input = current->input(i); if (input != NULL) { set_term_ref(list_append(out), input); included.insert(input); } } } } // Order results in the same order as the code. list_reverse(out); }
void block_update_pack_state_calls(Block* block) { if (block->stateType == NULL) { // No state type, make sure there's no pack_state call. // TODO: Handle this case properly (should search and destroy an existing pack_state call) return; } int stateOutputIndex = block->length() - 1 - find_state_output(block)->index; for (int i=0; i < block->length(); i++) { Term* term = block->get(i); if (term == NULL) continue; if (term->function == FUNCS.pack_state) { // Update the inputs for this pack_state call TermList inputs; list_inputs_to_pack_state(block, i, &inputs); set_inputs(term, inputs); } else if (should_have_preceeding_pack_state(term)) { // Check if we need to insert a pack_state call Term* existing = term->input(stateOutputIndex); if (existing == NULL || existing->function != FUNCS.pack_state) { TermList inputs; list_inputs_to_pack_state(block, i, &inputs); if (inputs.length() != 0) { Term* pack_state = apply(block, FUNCS.pack_state, inputs); move_before(pack_state, term); // Only set as an input for a non-minor block. if (term->nestedContents == NULL || !is_minor_block(term->nestedContents)) { set_input(term, stateOutputIndex, pack_state); set_input_hidden(term, stateOutputIndex, true); set_input_implicit(term, stateOutputIndex, true); } // Advance i to compensate for the term just added i++; } } } } }
void formatSource(caValue* source, Term* term) { format_name_binding(source, term); Block* contents = nested_contents(term); int index = 0; while (contents->get(index)->function == FUNCS.input) index++; bool firstCase = true; for (; index < contents->length(); index++) { Term* caseTerm = contents->get(index); if (caseTerm->function != FUNCS.case_func) break; if (is_hidden(caseTerm)) continue; append_phrase(source, caseTerm->stringProp("syntax:preWhitespace", ""), caseTerm, tok_Whitespace); if (firstCase) { append_phrase(source, "if ", caseTerm, sym_Keyword); format_source_for_input(source, caseTerm, 0); firstCase = false; } else if (caseTerm->input(0) != NULL) { append_phrase(source, "elif ", caseTerm, sym_Keyword); format_source_for_input(source, caseTerm, 0); } else append_phrase(source, "else", caseTerm, sym_None); // whitespace following the if/elif/else append_phrase(source, caseTerm->stringProp("syntax:lineEnding", ""), caseTerm, tok_Whitespace); format_block_source(source, nested_contents(caseTerm), caseTerm); } }
void branch_update_existing_pack_state_calls(Branch* branch) { if (branch->stateType == NULL) { // No state type, make sure there's no pack_state call. // TODO: Handle this case properly (should search and destroy an existing pack_state call) return; } int stateOutputIndex = branch->length() - 1 - find_state_output(branch)->index; for (int i=0; i < branch->length(); i++) { Term* term = branch->get(i); if (term == NULL) continue; if (term->function == FUNCS.pack_state) { // Update the inputs for this pack_state call TermList inputs; get_list_of_state_outputs(branch, i, &inputs); set_inputs(term, inputs); } if (term->function == FUNCS.exit_point) { // Check if we need to insert a pack_state call Term* existing = term->input(stateOutputIndex); if (existing == NULL || existing->function != FUNCS.pack_state) { TermList inputs; get_list_of_state_outputs(branch, i, &inputs); if (inputs.length() != 0) { Term* pack_state = apply(branch, FUNCS.pack_state, inputs); move_before(pack_state, term); set_input(term, stateOutputIndex + 1, pack_state); // Advance i to compensate for the term just added i++; } } } } }
Term* write_selector_for_accessor_chain(Branch* branch, TermList* chain) { TermList selectorInputs; // Skip index 0 - this is the head term. for (int i=1; i < chain->length(); i++) { Term* term = chain->get(i); if (term->function == FUNCS.get_index || term->function == FUNCS.get_field) { selectorInputs.append(term->input(1)); } else if (is_accessor_function(term)) { Term* element = create_string(branch, term->stringProp("syntax:functionName", "")); selectorInputs.append(element); } } return apply(branch, FUNCS.selector, selectorInputs); }
bool branch_state_type_is_out_of_date(Branch* branch) { // Alloc an array that tracks, for each field in the existing stateType, // whether we have found a corresponding term for that field. bool* typeFieldFound = NULL; int existingFieldCount = 0; if (branch->stateType != NULL) { existingFieldCount = compound_type_get_field_count(branch->stateType); size_t size = sizeof(bool) * existingFieldCount; typeFieldFound = (bool*) malloc(size); memset(typeFieldFound, 0, size); } // Walk through every term and check whether every unpack_state call is already // mentioned in the state type. for (int i=0; i < branch->length(); i++) { Term* term = branch->get(i); if (term == NULL) continue; if (term->function != FUNCS.unpack_state) continue; // Found an unpack_state call Term* identifyingTerm = term->input(1); // If the branch doesn't yet have a stateType then that's an update. if (branch->stateType == NULL) goto return_true; // Look for the field name int fieldIndex = list_find_field_index_by_name(branch->stateType, unique_name(identifyingTerm)); // If the name isn't found then that's an update if (fieldIndex == -1) goto return_true; // If the type doesn't match then that's an update if (compound_type_get_field_type(branch->stateType, fieldIndex) != declared_type(term)) goto return_true; // Record this field index as 'found' typeFieldFound[fieldIndex] = true; } // If there were any fields in the type that weren't found in the branch, then // that's an update. if (typeFieldFound != NULL) { for (int i=0; i < existingFieldCount; i++) { if (!typeFieldFound[i]) goto return_true; } } // No reason to update, return false. free(typeFieldFound); return false; return_true: free(typeFieldFound); return true; }
void for_loop_finish_iteration(Stack* stack, bool enableLoopOutput) { INCREMENT_STAT(LoopFinishIteration); Frame* frame = top_frame(stack); Branch* contents = frame->branch; // Find list length caValue* listInput = get_frame_register(frame, 0); // Increment the loop index caValue* index = get_top_register(stack, for_loop_find_index(contents)); set_int(index, as_int(index) + 1); // Preserve list output if (enableLoopOutput && frame->exitType != name_Discard) { caValue* outputIndex = get_frame_register(frame, for_loop_find_output_index(contents)); Term* outputPlaceholder = get_output_placeholder(contents, 0); caValue* outputList = get_frame_register(frame, outputPlaceholder); caValue* outputValue = find_stack_value_for_term(stack, outputPlaceholder->input(0), 0); if (!is_list(outputList)) set_list(outputList); list_touch(outputList); copy(outputValue, list_get(outputList, as_int(outputIndex))); INCREMENT_STAT(LoopWriteOutput); // Advance output index set_int(outputIndex, as_int(outputIndex) + 1); } // Check if we are finished if (as_int(index) >= list_length(listInput) || frame->exitType == name_Break || frame->exitType == name_Return) { // Possibly truncate output list, in case any elements were discarded. if (enableLoopOutput) { caValue* outputIndex = get_frame_register(frame, for_loop_find_output_index(contents)); Term* outputPlaceholder = get_output_placeholder(contents, 0); caValue* outputList = get_frame_register(frame, outputPlaceholder); list_resize(outputList, as_int(outputIndex)); } else { Term* outputPlaceholder = get_output_placeholder(contents, 0); caValue* outputList = get_frame_register(frame, outputPlaceholder); set_list(outputList, 0); } finish_frame(stack); return; } // If we're not finished yet, copy rebound outputs back to inputs. for (int i=1;; i++) { Term* input = get_input_placeholder(contents, i); if (input == NULL) break; Term* output = get_output_placeholder(contents, i); copy(get_frame_register(frame, output), get_frame_register(frame, input)); INCREMENT_STAT(Copy_LoopCopyRebound); } // Return to start of loop body frame->pc = 0; frame->nextPc = 0; frame->exitType = name_None; }
void Block__output(VM* vm) { Block* block = as_block(vm->input(0)); Term* placeholder = get_output_placeholder(block, vm->input(1)->as_i()); set_term_ref(vm->output(), placeholder->input(0)); }
void static_type_func(caStack* stack) { Term* caller = (Term*) circa_caller_term(stack); Term* input = caller->input(0); set_type(circa_output(stack, 0), input->type); }