diff --git a/include/blocks/stmt.h b/include/blocks/stmt.h index 6c5a560..d585328 100644 --- a/include/blocks/stmt.h +++ b/include/blocks/stmt.h @@ -2,11 +2,12 @@ #define STMT_H #include "blocks/expr.h" #include +#include namespace block { class stmt : public block { public: typedef std::shared_ptr Ptr; - std::string annotation; + std::set annotation; virtual void dump(std::ostream &, int) override; virtual void accept(block_visitor *a) override { a->visit(self()); diff --git a/include/builder/builder_base.h b/include/builder/builder_base.h index f0f0188..8e13026 100644 --- a/include/builder/builder_base.h +++ b/include/builder/builder_base.h @@ -26,36 +26,24 @@ std::vector extract_call_arguments_helper(const arg_types &... class builder { - typedef builder BT; - public: // All members here block::expr::Ptr block_expr; - typedef builder super; - // All the costructors and copy constructors to the top - // Simple constrcutor, should only be used inside the operator // and set the block_expr immediately builder() = default; // Copy constructor from another builder - builder(const BT &other) { + builder(const builder &other) { block_expr = other.block_expr; } static bool builder_precheck(void) { - assert(builder_context::current_builder_context != nullptr); - builder_context *ctx = builder_context::current_builder_context; - if (builder_context::current_builder_context->bool_vector.size() > 0) { - assert(ctx->expr_counter < ctx->expr_sequence.size()); - return true; - } - return false; + return get_run_state()->is_catching_up(); } void builder_from_sequence(void) { - builder_context *ctx = builder_context::current_builder_context; - block_expr = ctx->expr_sequence[ctx->expr_counter++]; + block_expr = get_run_state()->get_next_cached_expr(); } static builder create_builder_from_sequence(void) { builder ret_builder; @@ -63,8 +51,7 @@ class builder { return ret_builder; } static void push_to_sequence(block::expr::Ptr a) { - builder_context *ctx = builder_context::current_builder_context; - ctx->expr_sequence.push_back(a); + get_run_state()->add_to_cached_expr(a); } builder(const unsigned int &a) : builder((int)a) {} @@ -75,11 +62,11 @@ class builder { } block::int_const::Ptr int_const = std::make_shared(); - tracer::tag offset = get_offset_in_function(); + tracer::tag offset = tracer::get_offset_in_function(); int_const->static_offset = offset; int_const->value = a; int_const->is_64bit = false; - builder_context::current_builder_context->add_node_to_sequence(int_const); + get_run_state()->add_node_to_sequence(int_const); block_expr = int_const; push_to_sequence(block_expr); @@ -92,11 +79,11 @@ class builder { } block::int_const::Ptr int_const = std::make_shared(); - tracer::tag offset = get_offset_in_function(); + tracer::tag offset = tracer::get_offset_in_function(); int_const->static_offset = offset; int_const->value = a; int_const->is_64bit = true; - builder_context::current_builder_context->add_node_to_sequence(int_const); + get_run_state()->add_node_to_sequence(int_const); block_expr = int_const; push_to_sequence(block_expr); @@ -112,10 +99,10 @@ class builder { } block::double_const::Ptr double_const = std::make_shared(); - tracer::tag offset = get_offset_in_function(); + tracer::tag offset = tracer::get_offset_in_function(); double_const->static_offset = offset; double_const->value = a; - builder_context::current_builder_context->add_node_to_sequence(double_const); + get_run_state()->add_node_to_sequence(double_const); block_expr = double_const; push_to_sequence(block_expr); @@ -127,10 +114,10 @@ class builder { } block::float_const::Ptr float_const = std::make_shared(); - tracer::tag offset = get_offset_in_function(); + tracer::tag offset = tracer::get_offset_in_function(); float_const->static_offset = offset; float_const->value = a; - builder_context::current_builder_context->add_node_to_sequence(float_const); + get_run_state()->add_node_to_sequence(float_const); block_expr = float_const; push_to_sequence(block_expr); @@ -145,10 +132,10 @@ class builder { } block::string_const::Ptr string_const = std::make_shared(); - tracer::tag offset = get_offset_in_function(); + tracer::tag offset = tracer::get_offset_in_function(); string_const->static_offset = offset; string_const->value = s; - builder_context::current_builder_context->add_node_to_sequence(string_const); + get_run_state()->add_node_to_sequence(string_const); block_expr = string_const; push_to_sequence(block_expr); @@ -165,33 +152,33 @@ class builder { // Other basic functions template - BT builder_unary_op() const { + builder builder_unary_op() const { if (builder_precheck()) { return create_builder_from_sequence(); } - builder_context::current_builder_context->remove_node_from_sequence(block_expr); - tracer::tag offset = get_offset_in_function(); + get_run_state()->remove_node_from_sequence(block_expr); + tracer::tag offset = tracer::get_offset_in_function(); typename T::Ptr expr = std::make_shared(); expr->static_offset = offset; expr->expr1 = block_expr; - builder_context::current_builder_context->add_node_to_sequence(expr); + get_run_state()->add_node_to_sequence(expr); - BT ret_builder; + builder ret_builder; ret_builder.block_expr = expr; push_to_sequence(expr); return ret_builder; } template - BT builder_binary_op(const builder &a) const { + builder builder_binary_op(const builder &a) const { if (builder_precheck()) { return create_builder_from_sequence(); } - builder_context::current_builder_context->remove_node_from_sequence(block_expr); - builder_context::current_builder_context->remove_node_from_sequence(a.block_expr); + get_run_state()->remove_node_from_sequence(block_expr); + get_run_state()->remove_node_from_sequence(a.block_expr); - tracer::tag offset = get_offset_in_function(); + tracer::tag offset = tracer::get_offset_in_function(); typename T::Ptr expr = std::make_shared(); expr->static_offset = offset; @@ -199,22 +186,22 @@ class builder { expr->expr1 = block_expr; expr->expr2 = a.block_expr; - builder_context::current_builder_context->add_node_to_sequence(expr); + get_run_state()->add_node_to_sequence(expr); - BT ret_builder; + builder ret_builder; ret_builder.block_expr = expr; push_to_sequence(expr); return ret_builder; } - BT operator[](const BT &a) { + builder operator[](const builder &a) { if (builder_precheck()) { return create_builder_from_sequence(); } - builder_context::current_builder_context->remove_node_from_sequence(block_expr); - builder_context::current_builder_context->remove_node_from_sequence(a.block_expr); + get_run_state()->remove_node_from_sequence(block_expr); + get_run_state()->remove_node_from_sequence(a.block_expr); - tracer::tag offset = get_offset_in_function(); + tracer::tag offset = tracer::get_offset_in_function(); // assert(offset != -1); block::sq_bkt_expr::Ptr expr = std::make_shared(); @@ -223,27 +210,27 @@ class builder { expr->var_expr = block_expr; expr->index = a.block_expr; - builder_context::current_builder_context->add_node_to_sequence(expr); + get_run_state()->add_node_to_sequence(expr); - BT ret_builder; + builder ret_builder; ret_builder.block_expr = expr; push_to_sequence(expr); return ret_builder; } - BT operator*(void) { + builder operator*(void) { auto b = (*this)[0]; b.block_expr->template setMetadata("deref_is_star", true); return b; } - BT assign(const BT &a) { + builder assign(const builder &a) { if (builder_precheck()) { return create_builder_from_sequence(); } - builder_context::current_builder_context->remove_node_from_sequence(block_expr); - builder_context::current_builder_context->remove_node_from_sequence(a.block_expr); - tracer::tag offset = get_offset_in_function(); + get_run_state()->remove_node_from_sequence(block_expr); + get_run_state()->remove_node_from_sequence(a.block_expr); + tracer::tag offset = tracer::get_offset_in_function(); block::assign_expr::Ptr expr = std::make_shared(); expr->static_offset = offset; @@ -251,56 +238,45 @@ class builder { expr->var1 = block_expr; expr->expr1 = a.block_expr; - builder_context::current_builder_context->add_node_to_sequence(expr); + get_run_state()->add_node_to_sequence(expr); - BT ret_builder; + builder ret_builder; ret_builder.block_expr = expr; push_to_sequence(expr); return ret_builder; } - BT operator=(const BT &a) { + builder operator=(const builder &a) { return assign(a); } explicit operator bool() { - builder_context::current_builder_context->commit_uncommitted(); - return get_next_bool_from_context(builder_context::current_builder_context, block_expr); + get_run_state()->commit_uncommitted(); + return get_run_state()->get_next_bool(block_expr); } template - BT operator()(const arg_types &...args) { + builder operator()(const arg_types &...args) { if (builder_precheck()) { return create_builder_from_sequence(); } - builder_context::current_builder_context->remove_node_from_sequence(block_expr); - tracer::tag offset = get_offset_in_function(); + get_run_state()->remove_node_from_sequence(block_expr); + tracer::tag offset = tracer::get_offset_in_function(); block::function_call_expr::Ptr expr = std::make_shared(); expr->static_offset = offset; expr->expr1 = block_expr; - expr->args = extract_call_arguments(args...); + expr->args = extract_call_arguments(args...); std::reverse(expr->args.begin(), expr->args.end()); - builder_context::current_builder_context->add_node_to_sequence(expr); + get_run_state()->add_node_to_sequence(expr); - BT ret_builder; + builder ret_builder; ret_builder.block_expr = expr; push_to_sequence(expr); return ret_builder; } - template - void construct_builder_from_foreign_expr(const T &t) { - if (builder_precheck()) { - builder_from_sequence(); - return; - } - - block_expr = create_foreign_expr(t); - push_to_sequence(block_expr); - } - }; void annotate(std::string); @@ -323,8 +299,7 @@ extract_call_arguments_helper(const T &first_arg, const arg_types &...rest_args) template std::vector extract_call_arguments_helper(const BT &first_arg, const arg_types &...rest_args) { - assert(builder_context::current_builder_context != nullptr); - builder_context::current_builder_context->remove_node_from_sequence(first_arg.block_expr); + get_run_state()->remove_node_from_sequence(first_arg.block_expr); std::vector rest = extract_call_arguments_helper(rest_args...); rest.push_back(first_arg.block_expr); @@ -336,29 +311,5 @@ std::vector extract_call_arguments(const arg_types &...args) { return extract_call_arguments_helper(args...); } -// Helper functions to create foreign expressions of arbitrary types -// Theses should be only called from the function to cast the type to builder classes -template -block::expr::Ptr create_foreign_expr(const T t) { - assert(builder_context::current_builder_context != nullptr); - tracer::tag offset = get_offset_in_function(); - typename block::foreign_expr::Ptr expr = std::make_shared>(); - expr->static_offset = offset; - expr->inner_expr = t; - builder_context::current_builder_context->add_node_to_sequence(expr); - return expr; -} - -template -BT create_foreign_expr_builder(const T t) { - if (builder::builder_precheck()) { - return builder::create_builder_from_sequence(); - } - BT ret_builder; - ret_builder.block_expr = create_foreign_expr(t); - builder::push_to_sequence(ret_builder.block_expr); - return ret_builder; -} - } // namespace builder #endif diff --git a/include/builder/builder_context.h b/include/builder/builder_context.h index 6333831..aa13dff 100644 --- a/include/builder/builder_context.h +++ b/include/builder/builder_context.h @@ -4,6 +4,8 @@ #include "blocks/expr.h" #include "blocks/stmt.h" #include "builder/forward_declarations.h" +#include "builder/signature_extract.h" +#include "builder/run_states.h" #include #include #include @@ -13,123 +15,49 @@ namespace builder { -template -block::expr::Ptr create_foreign_expr(const T t); -template -builder create_foreign_expr_builder(const T t); - - -class tag_map { -public: - std::unordered_map map; -}; - void lambda_wrapper(std::function); void lambda_wrapper_close(void); -void coroutine_wrapper(std::function); -void coroutine_wrapper_close(void); - class builder_context { public: - // Purely static state for the entire program - static builder_context *current_builder_context; - static int debug_creation_counter; - - - // TODO: Consider separating different levels of run states into - // separate objects so managing it is easier - - // Per run state - std::function internal_stored_lambda; - std::list uncommitted_sequence; - block::stmt::Ptr ast; - block::stmt_block::Ptr current_block_stmt; - std::vector bool_vector; - std::unordered_set visited_offsets; - std::vector expr_sequence; - unsigned long long expr_counter = 0; - std::string current_label; - - std::vector static_var_tuples; - std::vector deferred_static_var_tuples; - - // Run shared state - tag_map _internal_tags; - tag_map *memoized_tags; - - // State shared across non-deterministic failures - std::unordered_map> *nd_state_map = nullptr; - std::unordered_map> _nd_state_map; - - void reset_for_nd_failure(); - - // Some members are out of the scope of the executions and are never reset - std::vector assume_variables; - block::func_decl::Ptr current_func_decl; - // Flags are just constants that shouldn't get updated anyway // Flags for controlling BuildIt extraction // and code generation behavior - bool use_memoization = true; bool run_rce = false; bool feature_unstructured = false; bool dynamic_use_cxx = false; std::string dynamic_compiler_flags = ""; std::string dynamic_header_includes = ""; - bool enable_d2x = false; - - bool is_visited_tag(tracer::tag &new_tag); - void erase_tag(tracer::tag &erase_tag); - - builder_context(tag_map *_map = nullptr) { - if (_map == nullptr) { - memoized_tags = &_internal_tags; - } else { - memoized_tags = _map; - } - current_block_stmt = nullptr; - ast = nullptr; - - debug_creation_counter++; - } - - void commit_uncommitted(void); - void remove_node_from_sequence(block::expr::Ptr); - void add_node_to_sequence(block::expr::Ptr); + bool enable_d2x = false; - void add_stmt_to_current_block(block::stmt::Ptr, bool check_for_conflicts = true); + void extract_function_ast_impl(invocation_state*); + block::stmt::Ptr extract_ast_from_run(run_state*); - block::stmt::Ptr extract_ast_from_function(void (*f)(void)) { - std::function l = f; - return extract_ast_from_lambda(l); + // Old API still used by some samples. TODO: phase out + + block::stmt::Ptr extract_ast_from_function(std::function func) { + invocation_state i_state; + i_state.generated_func_decl = std::make_shared(); + i_state.b_ctx = this; + i_state.invocation_function = func; + extract_function_ast_impl(&i_state); + return i_state.generated_func_decl->body; } - block::stmt::Ptr extract_ast_from_lambda(std::function); - block::stmt::Ptr extract_ast_from_function_impl(void); - block::stmt::Ptr extract_ast_from_function_internal(std::vector bl = std::vector()); template block::stmt::Ptr extract_function_ast(F func_input, std::string func_name, OtherArgs &&...other_args) { - current_func_decl = std::make_shared(); - current_func_decl->func_name = func_name; - // The extract_signature_from_lambda will update the return type - current_func_decl->body = extract_ast_from_lambda( - extract_signature_from_lambda::from(this, func_input, func_name, other_args...)); - return current_func_decl; + // Establish a invocation state + invocation_state i_state = extract_signature(func_input, std::forward(other_args)...); + i_state.b_ctx = this; + // Set the name of the function + i_state.generated_func_decl->func_name = func_name; + extract_function_ast_impl(&i_state); + return i_state.generated_func_decl; } - template - T *assume_variable(std::string name) { - T * new_asm_variable = new T(with_name(name)); - assume_variables.push_back(new_asm_variable); - - return new_asm_variable; - } - ~builder_context(); }; -bool get_next_bool_from_context(builder_context *context, block::expr::Ptr); -tracer::tag get_offset_in_function(void); } // namespace builder + #endif diff --git a/include/builder/dyn_var.h b/include/builder/dyn_var.h index 946d60e..219c2ae 100644 --- a/include/builder/dyn_var.h +++ b/include/builder/dyn_var.h @@ -129,24 +129,22 @@ class dyn_var_impl : public var { // dyn_var->preferred_name = utils::find_variable_name(this); return; } - assert(builder_context::current_builder_context != nullptr); - assert(builder_context::current_builder_context->current_block_stmt != nullptr); - builder_context::current_builder_context->commit_uncommitted(); + get_run_state()->commit_uncommitted(); block::var::Ptr dyn_var = std::make_shared(); dyn_var->var_type = create_block_type(); - tracer::tag offset = get_offset_in_function(); + tracer::tag offset = tracer::get_offset_in_function(); dyn_var->preferred_name = utils::find_variable_name_cached(this, offset); block_var = dyn_var; dyn_var->static_offset = offset; block_decl_stmt = nullptr; - if (builder_context::current_builder_context->bool_vector.size() > 0) + if (get_run_state()->is_catching_up()) return; block::decl_stmt::Ptr decl_stmt = std::make_shared(); decl_stmt->static_offset = offset; decl_stmt->decl_var = dyn_var; decl_stmt->init_expr = nullptr; block_decl_stmt = decl_stmt; - builder_context::current_builder_context->add_stmt_to_current_block(decl_stmt); + get_run_state()->add_stmt_to_current_block(decl_stmt, true); } dyn_var_impl() { @@ -170,7 +168,7 @@ class dyn_var_impl : public var { } // Basic and other constructors dyn_var_impl(const as_global &v) { - if (builder_context::current_builder_context == nullptr) { + if (!is_under_run()) { create_dyn_var(true); block_var->var_name = v.name; var_name = v.name; @@ -262,15 +260,18 @@ class dyn_var_impl : public var { } dyn_var_impl(const builder &a) { - builder_context::current_builder_context->remove_node_from_sequence(a.block_expr); + get_run_state()->remove_node_from_sequence(a.block_expr); create_dyn_var(); - if (builder_context::current_builder_context->bool_vector.size() > 0) + if (get_run_state()->is_catching_up()) return; block_decl_stmt->init_expr = a.block_expr; } template struct is_builder_constructible { + // TODO: Add more checks to is_base_of to + // make sure it is wrapping a type that is builder_constructible + // We could just check if a conversion to (builder) exists static const bool value = std::is_arithmetic::value || std::is_base_of::value || std::is_base_of::value; }; @@ -283,12 +284,11 @@ class dyn_var_impl : public var { dyn_var_impl(const char *s) : self_type((builder)(std::string)s) {} dyn_var_impl(char *s) : self_type((builder)(std::string)s) {} - dyn_var_impl(const std::initializer_list &_a) { - std::vector a(_a); - assert(builder_context::current_builder_context != nullptr); + void from_builder_vector(std::vector a) { + auto r_state = get_run_state(); for (unsigned int i = 0; i < a.size(); i++) { - builder_context::current_builder_context->remove_node_from_sequence(a[i].block_expr); + r_state->remove_node_from_sequence(a[i].block_expr); } create_dyn_var(); if (builder::builder_precheck()) { @@ -297,7 +297,7 @@ class dyn_var_impl : public var { block_decl_stmt->init_expr = bt.block_expr; return; } - tracer::tag offset = get_offset_in_function(); + tracer::tag offset = tracer::get_offset_in_function(); block::initializer_list_expr::Ptr list_expr = std::make_shared(); list_expr->static_offset = offset; for (unsigned int i = 0; i < a.size(); i++) { @@ -307,6 +307,24 @@ class dyn_var_impl : public var { builder::push_to_sequence(list_expr); } + dyn_var_impl(const std::initializer_list &_a) { + std::vector a(_a); + from_builder_vector(a); + } + + template + dyn_var_impl(const std::vector &_a, + typename std::enable_if::value>::type*_ = NULL ) { + + std::vector a; + // Create builder nodes for each elements, they would be pushed to sequence + // AND to the uncommitted list + for (auto i: _a) { + a.emplace_back(i); + } + from_builder_vector(a); + } + virtual ~dyn_var_impl() = default; // Assume that _impl objects will never be created @@ -502,7 +520,7 @@ class dyn_var: public dyn_var_impl { // so we can manually construct here. This allows to properly // handle init_expr if (wt.init_expr) - builder_context::current_builder_context->remove_node_from_sequence(wt.init_expr); + get_run_state()->remove_node_from_sequence(wt.init_expr); create_dyn_var(); block_var->var_type = wt.t.enclosed_type; if (block_decl_stmt) diff --git a/include/builder/forward_declarations.h b/include/builder/forward_declarations.h index 67e82c6..2918f1b 100644 --- a/include/builder/forward_declarations.h +++ b/include/builder/forward_declarations.h @@ -36,12 +36,6 @@ using is_dyn_var_type = std::is_base_of; template class type_extractor; -template -struct extract_signature; - -template -struct extract_signature_from_lambda; - // constructor helper to defer the initialization of dyn_var // This allows declaring dyn_var outside the context, but initialize // them later diff --git a/include/builder/nd_var.h b/include/builder/nd_var.h index 142ad90..b321adf 100644 --- a/include/builder/nd_var.h +++ b/include/builder/nd_var.h @@ -8,83 +8,107 @@ namespace builder { -template -struct nd_var { -}; - -struct nd_var_gen_base { -}; - -template -struct nd_var_gen: public nd_var_gen_base { -}; +/* The new API for nd_var-able objects is simple + We require the objects to follow data-flow lattice properties + Each ND var wrapped T can have whatever state it wants, but it needs to derive from + nd_var_base (we don't need to have separate generators any more). + nd_var_base objects will go across executions and will be stored + in the invocation state. + + The type T however needs to provide two functions : check(int) which checks if + a new value satisfies the current state of the object. merge(int) merges the new + value into the current state and also updates the generator. + +*/ -template <> -struct nd_var_gen: public nd_var_gen_base { - // false, // true - bool allowed_values[2] = {true, true}; +// Base class for nd_var wrappable objects +// We are not making any functions virtual but we declare them here to make sure +// users declare them too +class nd_var_base { +protected: + nd_var_base() = default; +public: + bool check(int e) { + assert(false && "Every derived type must define check"); + return false; + } + bool merge(int e) { + assert(false && "Every deried type must define merge"); + } }; - -template -std::shared_ptr> get_or_create_generator(tracer::tag req_tag) { - if (builder_context::current_builder_context->nd_state_map->find(req_tag) == - builder_context::current_builder_context->nd_state_map->end()) { - (*(builder_context::current_builder_context->nd_state_map))[req_tag] = std::make_shared>(); +template +std::shared_ptr get_or_create_generator(tracer::tag req_tag, Args&&...args) { + if (get_invocation_state()->nd_state_map.find(req_tag) == get_invocation_state()->nd_state_map.end()) { + get_invocation_state()->nd_state_map[req_tag] = std::make_shared(std::forward(args)...); } - - return std::static_pointer_cast>( - (*(builder_context::current_builder_context->nd_state_map))[req_tag]); + return std::static_pointer_cast(get_invocation_state()->nd_state_map[req_tag]); } -/* contstraints on bool types -Bool types are enumerable so it is suitable for nd_var -Since bool has only two values we don't need too many constraints. -The only valid constraint is requires_equal. +// A simple true at top boolean nd_var wrappable type +class true_top: public nd_var_base { +public: + typedef enum { + T = 1, + F = 0, + } value_t; -The lattice for bools also has just two points, -the order can be specified by initial preferred value -*/ + int value; + + true_top(int def): value(def) {} + true_top(): value((int)F) {} -template <> -struct nd_var { + bool check(int e) { + if (value == T) return true; + if (e == value) return true; + return false; + } + void merge(int e) { + if (e == F) return; + value = e; + } +}; - static_var current_value; - tracer::tag current_tag; - // Values can only be retrieved - operator bool () const { - return current_value; +template +class nd_var { + static_assert(std::is_base_of::value, "Types wrapped in nd_var must derive from nd_var_base"); + std::shared_ptr val; + +public: + template + nd_var(Args&&...args) { + tracer::tag t = tracer::get_offset_in_function(); + val = get_or_create_generator(t, std::forward(args)...); } - void sample(bool default_value = false) { - current_tag = get_offset_in_function(); - auto generator = get_or_create_generator(current_tag); - - if (generator->allowed_values[default_value] == true) - current_value = default_value; - else if (generator->allowed_values[!default_value] == true) - current_value = !default_value; - else - assert(false && "nd bool has no possible values allowed"); + // Allow access to wrapped value in case user wants to access object specific APIs + operator T& (void) { + return *val; + } + operator const T& (void) const { + return *val; } - nd_var(bool default_value = false) { - sample(default_value); + T* get(void) { + return val.get(); + } + const T* get(void) const { + return val.get(); } - - void require_equal(bool value) { - if (value == current_value) return; - // We have sampled a wrong value, time to update - // the generator and reset - auto generator = get_or_create_generator(current_tag); - generator->allowed_values[current_value] = false; - + void require_val(int e) { + // If the required value is compatible with the current state, + // return + if (val->check(e)) return; + // Otherwise, merge update and throw + val->merge(e); throw NonDeterministicFailureException(); } + }; + } #endif diff --git a/include/builder/run_states.h b/include/builder/run_states.h new file mode 100644 index 0000000..3c977a8 --- /dev/null +++ b/include/builder/run_states.h @@ -0,0 +1,194 @@ +#ifndef BUILDER_RUN_STATES_H +#define BUILDER_RUN_STATES_H + +#include "util/hash_utils.h" +#include +#include "util/tracer.h" +#include "builder/forward_declarations.h" +#include +#include +#include +#include +#include "blocks/stmt.h" + +namespace builder { + +// The classes in this header file will track all dynamic state +// extraction. This moves away from builder_context for tracking all +// state. The builder_context will now be purely used for read only +// parameters + +/* There are three levels of data structures maintained based on different +types of re-runs + +The first is a run_state which is for each execution of the lambda. The run_state +object tracks the generated instructions, the bool vector and the loopback tags. +This is also the global current run object and will have pointers to the other data +structures + +The second is the execution_state object which tracks the state for an entire program being +invoked. This holds the data structures shared across various runs of the same program that are +created to due to divergent control flow + +The final is the an invokation_state which is for maintaining state that is shared across different +runs due to nd_var conflicts. Each call from the user to extract_function_ast creates one of this object + +*/ + +class run_state; +class execution_state; +class invocation_state; +class builder_context; +class nd_var_base; + +class run_state { +public: + static run_state* current_run_state; +private: + + /* Generation related members */ + + // The main output block + block::stmt_block::Ptr current_stmt_block; + + // The uncommitted sequence of expressions + std::vector uncommitted_sequence; + + // Cached generated expressions to be reused for children executions + std::vector cached_expr_sequence; + unsigned int cached_expr_counter = 0; + + // Annotations to be attached to the next statement + std::set current_annotations; + + /* Tracing and re-execution related members */ + + // Vector of bools to return on branching + std::vector bool_vector; + + /* Tag creation fields */ + std::vector static_var_tuples; + std::vector deferred_static_var_tuples; + + + /* Memoization related fields */ + + // Tags visited before for loopback edges + std::unordered_set visited_offsets; + + /* Parent dynamic states */ + execution_state* e_state; + invocation_state* i_state; + +public: + // Run state cannot be created without parent states + run_state(execution_state* e_state, invocation_state* i_state): e_state(e_state), i_state(i_state) {} + + execution_state* get_e_state() { return e_state; } + invocation_state* get_i_state() { return i_state; } + + bool is_catching_up(void) { return bool_vector.size() > 0; } + block::expr::Ptr get_next_cached_expr() { + assert(cached_expr_counter < cached_expr_sequence.size()); + return cached_expr_sequence[cached_expr_counter++]; + } + void add_to_cached_expr(block::expr::Ptr a) { + cached_expr_sequence.push_back(a); + } + void add_annotation(std::string s) { + current_annotations.insert(s); + } + std::set get_and_clear_annotations(void) { + std::set to_ret = current_annotations; + current_annotations.clear(); + return to_ret; + } + + friend class execution_state; + friend class invocation_state; + friend class builder_context; + friend tracer::tag tracer::get_offset_in_function(void); + template + friend class static_var; + +private: + +public: + void add_stmt_to_current_block(block::stmt::Ptr, bool check_memoization); + void commit_uncommitted(void); + void remove_node_from_sequence(block::expr::Ptr); + void add_node_to_sequence(block::expr::Ptr); + bool is_visited_tag(tracer::tag &new_tag); + void erase_tag(tracer::tag &erase_tag); + bool get_next_bool(block::expr::Ptr); +}; + +class execution_state { + /* Memoization related fields */ + std::unordered_map memoized_tags; + + /* Parent dynamic states */ + invocation_state* i_state; + +public: + execution_state(invocation_state* i_state): i_state(i_state) {} + + friend class invocation_state; + friend class run_state; + friend class builder_context; + +}; + +class invocation_state { + /* ND_VAR state */ + std::unordered_map> nd_state_map; + + // Main invocation function + std::function invocation_function; + + // Generated function declaration + block::func_decl::Ptr generated_func_decl; + + builder_context* b_ctx = nullptr; + +public: + + builder_context* get_b_ctx() { return b_ctx; } + + friend class run_state; + friend class execution_state; + friend class builder_context; + + template + friend invocation_state extract_signature(F func, OtherArgs&&... args); + + template + friend struct extract_signature_impl; + + template + friend std::shared_ptr get_or_create_generator(tracer::tag req_tag, Args&&...args); +}; + +static inline run_state* get_run_state(void) { + assert(run_state::current_run_state != nullptr); + return run_state::current_run_state; +} + +static inline bool is_under_run(void) { + return run_state::current_run_state != nullptr; +} + +static inline execution_state* get_execution_state(void) { + return get_run_state()->get_e_state(); +} + +static inline invocation_state* get_invocation_state(void) { + return get_run_state()->get_i_state(); +} + +static inline builder_context* get_builder_context(void) { + return get_invocation_state()->get_b_ctx(); +} + +} +#endif diff --git a/include/builder/signature_extract.h b/include/builder/signature_extract.h index 8bb9fc1..16d117d 100644 --- a/include/builder/signature_extract.h +++ b/include/builder/signature_extract.h @@ -1,139 +1,151 @@ #ifndef SIGNATURE_EXTRACT_H #define SIGNATURE_EXTRACT_H -#include "builder/builder_context.h" -#include "builder/forward_declarations.h" -#include -#include + +#include "builder/run_states.h" +#include namespace builder { -template -struct peel_dyn; -template -struct peel_dyn::value>::type> { - typedef typename T::stored_type type; -}; +// extract_signature impl is a struct because we want to +// write partial specialization +/* +ProcessedArgTypes - A tuple of types from the function type that have already been processed +RemainingArgTypes - A tuple of types from the funciton type that are yet to be processed -struct extract_signature_enable; -template -struct filter_var_type { - constexpr static bool value = false; -}; +// Template arguments on the static members are for everything that can be implicitly inferred +F - invocable type - can be function or lambda +NextParam- the very next params the user provided, may or may not be used just yet +OtherParams... - all the remaining params the other + + +ProcessedArgTypes is borrowed from above and hence is not really a pack +*/ template -struct filter_var_type::value>::type> { - constexpr static bool value = true; -}; +constexpr bool is_dyn_var(void) { + return std::is_base_of::value; +} -// This template matches a dyn_var argument and pushes a new argument in the -// function declaration -template -struct extract_signature::value>::type, T, - FutureArgTypes...> { - template - struct from { - template - struct with { - static std::function call(builder_context *context, int arg_count, ClassType func, - RestArgTypes &...rest_args, OtherArgs &...other_args) { - std::string arg_name = "arg" + std::to_string(arg_count); - T *new_arg = context->assume_variable(arg_name); - context->current_func_decl->args.push_back(new_arg->block_var); - return extract_signature::template from< - RestArgTypes..., T &>::template with::call(context, arg_count + 1, - func, rest_args..., - *new_arg, other_args...); - } - }; - }; -}; +template +struct extract_signature_impl { -// This template specialization matches a non dyn_var argument and simply -// forwards values from other_args -template -struct extract_signature::value>::type, T, - FutureArgTypes...> { - template - struct from { - template - struct with { - static std::function call(builder_context *context, int arg_count, ClassType func, - RestArgTypes &...rest_args, TO &to, - OtherArgs &...other_args) { - return extract_signature::template from< - RestArgTypes..., TO &>::template with::call(context, arg_count + 1, - func, rest_args..., to, - other_args...); - } - }; - }; + // The ProcessedArgType is used as it is here, but in an actual implementation it will be an + // expansion from inside the tuple + template + static void fill_invocation(invocation_state* i_state, F func, int arg_index, + ProcessedArgTypes processed_arg, NextParam&& next_param, OtherParams&&...other_params); }; -template -struct extract_signature { - template - struct from { - template - struct with { - // At this point rest of the OtherArgs will just be - // thrown away - static std::function call(builder_context *context, int arg_count, ClassType func, - RestArgTypes &...rest_args) { - context->current_func_decl->return_type = - type_extractor::type>::extract_type(); - return [&, func](void) { create_return_stmt(func(rest_args...)); }; - } - }; - }; +// For partial specializations, the template parameter is a pack but is used inside a tuple +// 1. When the next argument to process is dyn_var - It has to be dyn_var not any derived types +template +struct extract_signature_impl, std::tuple, ReturnType, typename std::enable_if()>::type> { + using NextArg = typename NextArgWrap::stored_type; + // The dyn_var version doesn't need any NextParam to exist or be used + template + static void fill_invocation(invocation_state* i_state, F func, int arg_index, ProcessedArgTypes...processed_args, OtherParams&&...other_params) { + // We don't need to heap allocate objects anymore, just throw in a with_name, it has all + // the information to create the dyn_var when invoking the actual funciton + std::string arg_name = "arg" + std::to_string(arg_index); + auto var = std::make_shared(); + var->var_name = arg_name; + var->var_type = dyn_var::create_block_type(); + var->var_type->static_offset = var->static_offset = tracer::get_unique_tag(); + i_state->generated_func_decl->args.push_back(var); + // We explain below by with_name is wrapped in a tuple + extract_signature_impl>, std::tuple, ReturnType>::fill_invocation(i_state, func, arg_index + 1, std::forward(processed_args)..., std::tuple(with_name(arg_name)), std::forward(other_params)...); + } }; -template -struct extract_signature { - template - struct from { - template - struct with { - // At this point rest of the OtherArgs will just be - // thrown away - static std::function call(builder_context *context, int arg_count, ClassType func, - RestArgTypes &...rest_args) { - block::scalar_type::Ptr type = std::make_shared(); - type->scalar_type_id = block::scalar_type::VOID_TYPE; - context->current_func_decl->return_type = type; - return [&, func](void) { func(rest_args...); }; - } - }; - }; +// 2. When the next argumetn to process isn't a dyn_var +template +struct extract_signature_impl, std::tuple, ReturnType, typename std::enable_if()>::type> { + // This version needs a NextParam + template + static void fill_invocation(invocation_state* i_state, F func, int arg_index, ProcessedArgTypes...processed_args, NextParam&& next_param, OtherParams&&...other_params) { + // No with name this time, just throw in the arg into processed args + // Pass it as NextParam type, not NextArg type. Defer all conversions till the final call + // We wrap the NextParam type in a tuple so that types that are references so that the arguments can be captured by + // value in the lambda. This way references remain references. If we passed the reference along directly, it becomes a copy + // We also don't wrap NextParam&& because rvalue references cannot be wrapped in a tuple (if it needs to be copyable) + extract_signature_impl>, std::tuple, ReturnType>::fill_invocation(i_state, func, arg_index, std::forward(processed_args)..., std::tuple(std::forward(next_param)), std::forward(other_params)...); + } }; -template -struct extract_signature_from_lambda; - -// Match for lambda -template -struct extract_signature_from_lambda : public extract_signature_from_lambda {}; +// 3. When there aren't any more args to fill AND the return type is void +// No remaining types now +template +struct extract_signature_impl, std::tuple<>, void, void> { -template -struct extract_signature_from_lambda { - static std::function from(builder_context *context, ClassType func, std::string func_name, - OtherArgs &...other_args) { - return extract_signature::template from<>::template with::call(context, 0, func, - other_args...); + // Take other_args but discard them + template + static void fill_invocation(invocation_state* i_state, F func, int arg_index, ProcessedArgTypes...processed_args, OtherParams&&...other_params) { + // All conversions from with_name to dyn_var will happen INSIDE the lambda and hence the lifetype of the + // dyn_var will be valid for the run + // Mark the lambda to be mutable otherwise if func accepts a reference it won't bind correctly + // since captured variables are const when captured by value + auto vtype = std::make_shared(); + vtype->scalar_type_id = block::scalar_type::VOID_TYPE; + i_state->generated_func_decl->return_type = vtype; + // Since each argument is wrapped in a tuple, we can expand the tuple and capture + // the tuple by value + i_state->invocation_function = [=]() -> void {func(std::get<0>(processed_args)...);}; } }; -// Match for functions -template -struct extract_signature_from_lambda { - static std::function from(builder_context *context, ReturnType (*func)(Args...), - std::string func_name, OtherArgs &...other_args) { - return extract_signature::template from<>::template with::call(context, 0, func, - other_args...); +// 4. When there aren't any more args to fill and return type is not void +template +struct extract_signature_impl, std::tuple<>, ReturnType, void> { + template + static void fill_invocation(invocation_state* i_state, F func, int arg_index, ProcessedArgTypes...processed_args, OtherParams&&...other_params) { + // same reason as mutable as above + i_state->generated_func_decl->return_type = ReturnType::create_block_type(); + // Same reason why tuple are expanded above + i_state->invocation_function = [=]() -> void {create_return_stmt(func(std::get<0>(processed_args)...));}; } }; -} // namespace builder + +// f_type_helper extracts argument and return type from lambda or function + +// Default case matches for lambdas and redirects to "pointer to member function" +template +struct f_type_helper: public f_type_helper {}; + +// Case for pointer to member function (the actual operator() on lambdas) but the const version +template +struct f_type_helper: f_type_helper {}; + +// Same as above except non-cosnt for lambdas declared mutable +template +struct f_type_helper: f_type_helper {}; + +// Case for regular function and pointer types +template +struct f_type_helper: public f_type_helper {}; + +template +struct f_type_helper { + using arg_types = std::tuple; + using ret_type = ReturnType; +}; + +// Main entry point for extract_signature +template +invocation_state extract_signature(F func, OtherArgs&&... args) { + using ArgTypes = typename f_type_helper::arg_types; + using ReturnType = typename f_type_helper::ret_type; + + // Create an empty function decl + auto func_decl = std::make_shared(); + invocation_state i_state; + i_state.generated_func_decl = func_decl; + + extract_signature_impl, ArgTypes, ReturnType>::fill_invocation(&i_state, func, 0, std::forward(args)...); + + return i_state; +} + +} #endif diff --git a/include/builder/static_var.h b/include/builder/static_var.h index 7c00313..cb714b0 100644 --- a/include/builder/static_var.h +++ b/include/builder/static_var.h @@ -40,7 +40,7 @@ class static_var : static_var_base { mutable bool name_checked = false; void try_get_name() const { - if (builder_context::current_builder_context->enable_d2x == false) + if (get_builder_context()->enable_d2x == false) return; if (var_name == "" && name_checked == false) { var_name = utils::find_variable_name(const_cast(static_cast(this))); @@ -84,13 +84,11 @@ class static_var : static_var_base { return t; } static_var() { - assert(builder_context::current_builder_context != nullptr); - builder_context::current_builder_context->static_var_tuples.push_back(this); + get_run_state()->static_var_tuples.push_back(this); try_get_name(); } static_var(const T &v) { - assert(builder_context::current_builder_context != nullptr); - builder_context::current_builder_context->static_var_tuples.push_back(this); + get_run_state()->static_var_tuples.push_back(this); val = v; try_get_name(); } @@ -100,13 +98,12 @@ class static_var : static_var_base { is_deferred = true; } void deferred_init(void) { - assert(builder_context::current_builder_context != nullptr); // Deferred static variables are kept separate because they are never untracked // in the destructor // Even though the usual static vars can be destroyed out of order, these might create // unncessary bubbles in the queue // TODO: Consider merging these two - builder_context::current_builder_context->deferred_static_var_tuples.push_back(this); + get_run_state()->deferred_static_var_tuples.push_back(this); try_get_name(); } @@ -116,9 +113,10 @@ class static_var : static_var_base { return; } - assert(builder_context::current_builder_context != nullptr); - assert(builder_context::current_builder_context->static_var_tuples.size() > 0); + auto r_state = get_run_state(); + assert(r_state->static_var_tuples.size() > 0); + /* Instead of assuming that the last variable is the static_var we are destroying, we find the appropriate one, remove it and replace it with nullptr. During tag creation nullptrs are handled properly @@ -128,18 +126,18 @@ class static_var : static_var_base { This allows out of order destruction while still maintaining good static tags */ int index = -1; - for (int i = builder_context::current_builder_context->static_var_tuples.size() - 1; i >= 0; i--) { - if (builder_context::current_builder_context->static_var_tuples[i] == this) { + for (int i = r_state->static_var_tuples.size() - 1; i >= 0; i--) { + if (r_state->static_var_tuples[i] == this) { index = i; - builder_context::current_builder_context->static_var_tuples[i] = nullptr; + r_state->static_var_tuples[i] = nullptr; break; } } assert(index != -1 && "Static variable to destroy not valid"); - while (!builder_context::current_builder_context->static_var_tuples.empty() - && builder_context::current_builder_context->static_var_tuples.back() == nullptr) { - builder_context::current_builder_context->static_var_tuples.pop_back(); + while (!r_state->static_var_tuples.empty() + && r_state->static_var_tuples.back() == nullptr) { + r_state->static_var_tuples.pop_back(); } } @@ -178,19 +176,17 @@ class static_var : static_var_base { } static_var() { var_name = "ArrayVar"; - assert(builder_context::current_builder_context != nullptr); // This val _should_ not be used. But we will insert it to hold place // for this static var in the list of tuples, otherwise destructor order will be weird val = new T[1]; actual_size = 1; - builder_context::current_builder_context->static_var_tuples.push_back(this); + get_run_state()->static_var_tuples.push_back(this); } static_var(const std::initializer_list &list) { var_name = "ArrayVar"; - assert(builder_context::current_builder_context != nullptr); val = new T[list.size()]; actual_size = list.size(); - builder_context::current_builder_context->static_var_tuples.push_back(this); + get_run_state()->static_var_tuples.push_back(this); for (int i = 0; i < list.size(); i++) { val[i] = list[i]; } @@ -201,24 +197,24 @@ class static_var : static_var_base { delete[] val; val = new_ptr; actual_size = s; - // tracking tuples dont' need to be changed anymore + // tracking tuples dont' need to be changed anymore since we are tracking the static_var itself } ~static_var() { - assert(builder_context::current_builder_context != nullptr); - assert(builder_context::current_builder_context->static_var_tuples.size() > 0); + auto r_state = get_run_state(); + assert(r_state->static_var_tuples.size() > 0); int index = -1; - for (int i = builder_context::current_builder_context->static_var_tuples.size() - 1; i >= 0; i--) { - if (builder_context::current_builder_context->static_var_tuples[i] == this) { + for (int i = r_state->static_var_tuples.size() - 1; i >= 0; i--) { + if (r_state->static_var_tuples[i] == this) { index = i; - builder_context::current_builder_context->static_var_tuples[i] = nullptr; + r_state->static_var_tuples[i] = nullptr; break; } } assert(index != -1 && "Static variable to destroy not valid"); - while (!builder_context::current_builder_context->static_var_tuples.empty() - && builder_context::current_builder_context->static_var_tuples.back() == nullptr) { - builder_context::current_builder_context->static_var_tuples.pop_back(); + while (!r_state->static_var_tuples.empty() + && r_state->static_var_tuples.back() == nullptr) { + r_state->static_var_tuples.pop_back(); } delete[] val; } diff --git a/include/util/hash_utils.h b/include/util/hash_utils.h index 5a945a1..42fbb3d 100644 --- a/include/util/hash_utils.h +++ b/include/util/hash_utils.h @@ -2,6 +2,7 @@ #define BUILDER_HASH_UTILS_H #include "util/mtp_utils.h" +#include namespace tracer { // Just a hash_combine function diff --git a/include/util/tracer.h b/include/util/tracer.h index 1493c73..76af0c8 100644 --- a/include/util/tracer.h +++ b/include/util/tracer.h @@ -90,7 +90,7 @@ class tag { tag get_unique_tag(void); -tag get_offset_in_function_impl(builder::builder_context *current_builder_context); +tag get_offset_in_function(void); } // namespace tracer diff --git a/samples/outputs.var_names/sample15 b/samples/outputs.var_names/sample15 index 7285e1e..85e3bfc 100644 --- a/samples/outputs.var_names/sample15 +++ b/samples/outputs.var_names/sample15 @@ -1,10 +1,11 @@ STMT_BLOCK DECL_STMT - FUNCITON_TYPE - SCALAR_TYPE (VOID) - SCALAR_TYPE (INT) - POINTER_TYPE + POINTER_TYPE + FUNCITON_TYPE + SCALAR_TYPE (VOID) SCALAR_TYPE (INT) + POINTER_TYPE + SCALAR_TYPE (INT) VAR (bar_0) NO_INITIALIZATION DECL_STMT @@ -30,8 +31,8 @@ STMT_BLOCK VAR_EXPR VAR (b_3) { - void (*bar_0)(int, int*); - int (*bar2_1)(); + void (* bar_0)(int, int*); + int bar2_1(); int a_2; int* b_3; bar_0(a_2, b_3); diff --git a/samples/outputs.var_names/sample17 b/samples/outputs.var_names/sample17 index 9882ee1..dd92d0e 100644 --- a/samples/outputs.var_names/sample17 +++ b/samples/outputs.var_names/sample17 @@ -2,7 +2,7 @@ #include void print_value(int x) {printf("%c", x);} int get_value(void) {char x; scanf(" %c", &x); return x;} -int main(int argc, char* argv[]) { +void main (int arg0, char* arg1[]) { int pointer_0 = 0; int tape_1[256] = {0}; tape_1[pointer_0] = (tape_1[pointer_0] + 1) % 256; @@ -112,3 +112,4 @@ int main(int argc, char* argv[]) { tape_1[pointer_0] = (tape_1[pointer_0] + 1) % 256; print_value(tape_1[pointer_0]); } + diff --git a/samples/outputs.var_names/sample21 b/samples/outputs.var_names/sample21 index 7dc77a0..16bcc05 100644 --- a/samples/outputs.var_names/sample21 +++ b/samples/outputs.var_names/sample21 @@ -7,6 +7,6 @@ STMT_BLOCK SCALAR_TYPE (INT) VAR (bar_1) PLUS_EXPR - FOREIGN_EXPR + STRING_CONST ("foo") VAR_EXPR VAR (x_0) diff --git a/samples/outputs.var_names/sample30 b/samples/outputs.var_names/sample30 index 46647b4..45ac78a 100644 --- a/samples/outputs.var_names/sample30 +++ b/samples/outputs.var_names/sample30 @@ -92,7 +92,7 @@ STMT_BLOCK void* m_12; char n_13[] = "Hello world"; n_13 = "new string"; - char const* const volatile o_14 = "Hello world"; + const char* const volatile o_14 = "Hello world"; bool p_15 = 1; int x_16 = 0; } diff --git a/samples/outputs.var_names/sample31 b/samples/outputs.var_names/sample31 index 8353df0..b5cd74c 100644 --- a/samples/outputs.var_names/sample31 +++ b/samples/outputs.var_names/sample31 @@ -3,39 +3,33 @@ FUNC_DECL (func1) VAR (arg0) STMT_BLOCK DECL_STMT - SCALAR_TYPE (INT) - VAR (var0) - VAR_EXPR - VAR (arg0) - DECL_STMT - SCALAR_TYPE (INT) - VAR (z_1) + NAMED_TYPE (foo) + VAR (z_0) PLUS_EXPR PLUS_EXPR INT_CONST (3) MEMBER_ACCESS_EXPR (var1) VAR_EXPR - VAR (var0) + VAR (arg0) MUL_EXPR INT_CONST (5) MEMBER_ACCESS_EXPR (var1) VAR_EXPR - VAR (var0) + VAR (arg0) DECL_STMT SCALAR_TYPE (INT) - VAR (var2) + VAR (var1) PLUS_EXPR MEMBER_ACCESS_EXPR (neighbor) VAR_EXPR - VAR (z_1) + VAR (z_0) INT_CONST (2) RETURN_STMT VAR_EXPR - VAR (var2) -int func1 (int arg0) { - int var0 = arg0; - int z_1 = (3 + var0.var1) + (5 * var0.var1); - int var2 = z_1.neighbor + 2; - return var2; + VAR (var1) +int func1 (foo arg0) { + foo z_0 = (3 + arg0.var1) + (5 * arg0.var1); + int var1 = z_0.neighbor + 2; + return var1; } diff --git a/samples/outputs.var_names/sample37 b/samples/outputs.var_names/sample37 index 56e0d1c..c05bfcf 100644 --- a/samples/outputs.var_names/sample37 +++ b/samples/outputs.var_names/sample37 @@ -1,11 +1,10 @@ void __global__ cuda_kernel_0 (int* arg0) { - int thread_id_3 = (blockIdx.x * 512) + threadIdx.x; - arg0[thread_id_3] = 0; + int thread_id_2 = (blockIdx.x * 512) + threadIdx.x; + arg0[thread_id_2] = 0; } void bar (int* arg0) { - int* var0 = arg0; - cuda_kernel_0<<<128, 512>>>(var0); + cuda_kernel_0<<<128, 512>>>(arg0); cudaDeviceSynchronize(); } diff --git a/samples/outputs.var_names/sample40 b/samples/outputs.var_names/sample40 index e311bba..49d4436 100644 --- a/samples/outputs.var_names/sample40 +++ b/samples/outputs.var_names/sample40 @@ -1,54 +1,53 @@ #include -int match_re (char* arg1) { - int var3; - int var5; - char* var0 = arg1; - int str_len_1 = strlen(arg1); - int to_match_2 = 0; - if (to_match_2 < str_len_1) { - if (arg1[to_match_2] == 97) { - to_match_2 = to_match_2 + 1; +int match_re (char* arg0) { + int var2; + int var4; + int str_len_0 = strlen(arg0); + int to_match_1 = 0; + if (to_match_1 < str_len_0) { + if (arg0[to_match_1] == 97) { + to_match_1 = to_match_1 + 1; label0: - if (to_match_2 < str_len_1) { - if (var0[to_match_2] == 98) { - to_match_2 = to_match_2 + 1; + if (to_match_1 < str_len_0) { + if (arg0[to_match_1] == 98) { + to_match_1 = to_match_1 + 1; goto label0; } - if (var0[to_match_2] == 99) { + if (arg0[to_match_1] == 99) { label1: - to_match_2 = to_match_2 + 1; - if (to_match_2 < str_len_1) { - if (var0[to_match_2] == 99) { + to_match_1 = to_match_1 + 1; + if (to_match_1 < str_len_0) { + if (arg0[to_match_1] == 99) { goto label1; } - if (var0[to_match_2] == 100) { + if (arg0[to_match_1] == 100) { label2: - to_match_2 = to_match_2 + 1; - if (to_match_2 < str_len_1) { - var3 = 0; - return var3; + to_match_1 = to_match_1 + 1; + if (to_match_1 < str_len_0) { + var2 = 0; + return var2; } return 1; } - var3 = 0; - return var3; + var2 = 0; + return var2; } - var5 = 0; - return var5; + var4 = 0; + return var4; } - if (var0[to_match_2] == 100) { + if (arg0[to_match_1] == 100) { goto label2; } - var3 = 0; - return var3; + var2 = 0; + return var2; } - var5 = 0; - return var5; + var4 = 0; + return var4; } - var3 = 0; - return var3; + var2 = 0; + return var2; } - var5 = 0; - return var5; + var4 = 0; + return var4; } diff --git a/samples/outputs.var_names/sample58 b/samples/outputs.var_names/sample58 index e857cdf..e3dc1d4 100644 --- a/samples/outputs.var_names/sample58 +++ b/samples/outputs.var_names/sample58 @@ -4,15 +4,9 @@ FUNC_DECL (bar) STMT_BLOCK DECL_STMT SCALAR_TYPE (INT) - VAR (var0) - VAR_EXPR - VAR (arg0) - DECL_STMT - SCALAR_TYPE (INT) - VAR (y_1) + VAR (y_0) NO_INITIALIZATION void bar (int arg0) { - int var0 = arg0; - int y_1; + int y_0; } diff --git a/samples/outputs.var_names/sample64 b/samples/outputs.var_names/sample64 index 8f813ce..a07c994 100644 --- a/samples/outputs.var_names/sample64 +++ b/samples/outputs.var_names/sample64 @@ -1,15 +1,15 @@ int power (int arg0, int arg1) { - int exponent_3 = arg1; - int res_4 = 1; - int x_5 = arg0; - while (exponent_3 > 1) { - if ((exponent_3 % 2) == 1) { - res_4 = res_4 * x_5; + int exponent_0 = arg1; + int res_1 = 1; + int x_2 = arg0; + while (exponent_0 > 1) { + if ((exponent_0 % 2) == 1) { + res_1 = res_1 * x_2; } - x_5 = x_5 * x_5; - exponent_3 = exponent_3 / 2; + x_2 = x_2 * x_2; + exponent_0 = exponent_0 / 2; } - return res_4 * x_5; + return res_1 * x_2; } void bar (void) { diff --git a/samples/outputs.var_names/sample66 b/samples/outputs.var_names/sample66 new file mode 100644 index 0000000..fb434fb --- /dev/null +++ b/samples/outputs.var_names/sample66 @@ -0,0 +1,7 @@ +void bar (void) { + int y_0[] = {1, 2, 3}; + for (int i_1 = 0; i_1 < 3ll; i_1 = i_1 + 1) { + y_0[i_1] = y_0[i_1] + y_0[i_1 - 1]; + } +} + diff --git a/samples/outputs/sample15 b/samples/outputs/sample15 index 5305b91..bd56b89 100644 --- a/samples/outputs/sample15 +++ b/samples/outputs/sample15 @@ -1,10 +1,11 @@ STMT_BLOCK DECL_STMT - FUNCITON_TYPE - SCALAR_TYPE (VOID) - SCALAR_TYPE (INT) - POINTER_TYPE + POINTER_TYPE + FUNCITON_TYPE + SCALAR_TYPE (VOID) SCALAR_TYPE (INT) + POINTER_TYPE + SCALAR_TYPE (INT) VAR (var0) NO_INITIALIZATION DECL_STMT @@ -30,8 +31,8 @@ STMT_BLOCK VAR_EXPR VAR (var3) { - void (*var0)(int, int*); - int (*var1)(); + void (* var0)(int, int*); + int var1(); int var2; int* var3; var0(var2, var3); diff --git a/samples/outputs/sample17 b/samples/outputs/sample17 index 84d3755..9cbd927 100644 --- a/samples/outputs/sample17 +++ b/samples/outputs/sample17 @@ -2,7 +2,7 @@ #include void print_value(int x) {printf("%c", x);} int get_value(void) {char x; scanf(" %c", &x); return x;} -int main(int argc, char* argv[]) { +void main (int arg0, char* arg1[]) { int var0 = 0; int var1[256] = {0}; var1[var0] = (var1[var0] + 1) % 256; @@ -112,3 +112,4 @@ int main(int argc, char* argv[]) { var1[var0] = (var1[var0] + 1) % 256; print_value(var1[var0]); } + diff --git a/samples/outputs/sample21 b/samples/outputs/sample21 index 59ebf91..d56e7fa 100644 --- a/samples/outputs/sample21 +++ b/samples/outputs/sample21 @@ -7,6 +7,6 @@ STMT_BLOCK SCALAR_TYPE (INT) VAR (var1) PLUS_EXPR - FOREIGN_EXPR + STRING_CONST ("foo") VAR_EXPR VAR (var0) diff --git a/samples/outputs/sample30 b/samples/outputs/sample30 index 2be7191..4ada156 100644 --- a/samples/outputs/sample30 +++ b/samples/outputs/sample30 @@ -92,7 +92,7 @@ STMT_BLOCK void* var12; char var13[] = "Hello world"; var13 = "new string"; - char const* const volatile var14 = "Hello world"; + const char* const volatile var14 = "Hello world"; bool var15 = 1; int var16 = 0; } diff --git a/samples/outputs/sample31 b/samples/outputs/sample31 index fa00337..34e5e51 100644 --- a/samples/outputs/sample31 +++ b/samples/outputs/sample31 @@ -3,39 +3,33 @@ FUNC_DECL (func1) VAR (arg0) STMT_BLOCK DECL_STMT - SCALAR_TYPE (INT) + NAMED_TYPE (foo) VAR (var0) - VAR_EXPR - VAR (arg0) - DECL_STMT - SCALAR_TYPE (INT) - VAR (var1) PLUS_EXPR PLUS_EXPR INT_CONST (3) MEMBER_ACCESS_EXPR (var1) VAR_EXPR - VAR (var0) + VAR (arg0) MUL_EXPR INT_CONST (5) MEMBER_ACCESS_EXPR (var1) VAR_EXPR - VAR (var0) + VAR (arg0) DECL_STMT SCALAR_TYPE (INT) - VAR (var2) + VAR (var1) PLUS_EXPR MEMBER_ACCESS_EXPR (neighbor) VAR_EXPR - VAR (var1) + VAR (var0) INT_CONST (2) RETURN_STMT VAR_EXPR - VAR (var2) -int func1 (int arg0) { - int var0 = arg0; - int var1 = (3 + var0.var1) + (5 * var0.var1); - int var2 = var1.neighbor + 2; - return var2; + VAR (var1) +int func1 (foo arg0) { + foo var0 = (3 + arg0.var1) + (5 * arg0.var1); + int var1 = var0.neighbor + 2; + return var1; } diff --git a/samples/outputs/sample37 b/samples/outputs/sample37 index d88bab2..d6a3531 100644 --- a/samples/outputs/sample37 +++ b/samples/outputs/sample37 @@ -1,11 +1,10 @@ void __global__ cuda_kernel_0 (int* arg0) { - int var3 = (blockIdx.x * 512) + threadIdx.x; - arg0[var3] = 0; + int var2 = (blockIdx.x * 512) + threadIdx.x; + arg0[var2] = 0; } void bar (int* arg0) { - int* var0 = arg0; - cuda_kernel_0<<<128, 512>>>(var0); + cuda_kernel_0<<<128, 512>>>(arg0); cudaDeviceSynchronize(); } diff --git a/samples/outputs/sample40 b/samples/outputs/sample40 index 2566512..e7ea5a8 100644 --- a/samples/outputs/sample40 +++ b/samples/outputs/sample40 @@ -1,54 +1,53 @@ #include -int match_re (char* arg1) { - int var3; - int var5; - char* var0 = arg1; - int var1 = strlen(arg1); - int var2 = 0; - if (var2 < var1) { - if (arg1[var2] == 97) { - var2 = var2 + 1; +int match_re (char* arg0) { + int var2; + int var4; + int var0 = strlen(arg0); + int var1 = 0; + if (var1 < var0) { + if (arg0[var1] == 97) { + var1 = var1 + 1; label0: - if (var2 < var1) { - if (var0[var2] == 98) { - var2 = var2 + 1; + if (var1 < var0) { + if (arg0[var1] == 98) { + var1 = var1 + 1; goto label0; } - if (var0[var2] == 99) { + if (arg0[var1] == 99) { label1: - var2 = var2 + 1; - if (var2 < var1) { - if (var0[var2] == 99) { + var1 = var1 + 1; + if (var1 < var0) { + if (arg0[var1] == 99) { goto label1; } - if (var0[var2] == 100) { + if (arg0[var1] == 100) { label2: - var2 = var2 + 1; - if (var2 < var1) { - var3 = 0; - return var3; + var1 = var1 + 1; + if (var1 < var0) { + var2 = 0; + return var2; } return 1; } - var3 = 0; - return var3; + var2 = 0; + return var2; } - var5 = 0; - return var5; + var4 = 0; + return var4; } - if (var0[var2] == 100) { + if (arg0[var1] == 100) { goto label2; } - var3 = 0; - return var3; + var2 = 0; + return var2; } - var5 = 0; - return var5; + var4 = 0; + return var4; } - var3 = 0; - return var3; + var2 = 0; + return var2; } - var5 = 0; - return var5; + var4 = 0; + return var4; } diff --git a/samples/outputs/sample58 b/samples/outputs/sample58 index 837de4b..687f924 100644 --- a/samples/outputs/sample58 +++ b/samples/outputs/sample58 @@ -5,14 +5,8 @@ FUNC_DECL (bar) DECL_STMT SCALAR_TYPE (INT) VAR (var0) - VAR_EXPR - VAR (arg0) - DECL_STMT - SCALAR_TYPE (INT) - VAR (var1) NO_INITIALIZATION void bar (int arg0) { - int var0 = arg0; - int var1; + int var0; } diff --git a/samples/outputs/sample64 b/samples/outputs/sample64 index 42314c2..3d0d861 100644 --- a/samples/outputs/sample64 +++ b/samples/outputs/sample64 @@ -1,15 +1,15 @@ int power (int arg0, int arg1) { - int var3 = arg1; - int var4 = 1; - int var5 = arg0; - while (var3 > 1) { - if ((var3 % 2) == 1) { - var4 = var4 * var5; + int var0 = arg1; + int var1 = 1; + int var2 = arg0; + while (var0 > 1) { + if ((var0 % 2) == 1) { + var1 = var1 * var2; } - var5 = var5 * var5; - var3 = var3 / 2; + var2 = var2 * var2; + var0 = var0 / 2; } - return var4 * var5; + return var1 * var2; } void bar (void) { diff --git a/samples/outputs/sample66 b/samples/outputs/sample66 new file mode 100644 index 0000000..d589fd5 --- /dev/null +++ b/samples/outputs/sample66 @@ -0,0 +1,7 @@ +void bar (void) { + int var0[] = {1, 2, 3}; + for (int var1 = 0; var1 < 3ll; var1 = var1 + 1) { + var0[var1] = var0[var1] + var0[var1 - 1]; + } +} + diff --git a/samples/sample15.cpp b/samples/sample15.cpp index 3bc76f0..a794121 100644 --- a/samples/sample15.cpp +++ b/samples/sample15.cpp @@ -7,7 +7,7 @@ using builder::dyn_var; // A simple function_var declaration static void foo(void) { - dyn_var bar; + dyn_var bar; dyn_var bar2; dyn_var a; diff --git a/samples/sample16.cpp b/samples/sample16.cpp index 962823e..8ab0f37 100644 --- a/samples/sample16.cpp +++ b/samples/sample16.cpp @@ -6,15 +6,15 @@ using builder::dyn_var; // A simple example with an assumed variable in the builder context -dyn_var *assumed_variable_ref; +// Assumed variables are now deprecated and one can simply use a global +dyn_var assumed_variable = builder::with_name("global_var1"); static void foo(void) { dyn_var a; a = 20; - *assumed_variable_ref = 10 + a; + assumed_variable = 10 + a; } int main(int argc, char *argv[]) { builder::builder_context context; - assumed_variable_ref = context.assume_variable>("global_var1"); auto ast = context.extract_ast_from_function(foo); ast->dump(std::cout, 0); block::c_code_generator::generate_code(ast, std::cout, 0); diff --git a/samples/sample17.cpp b/samples/sample17.cpp index 110765f..fa45df0 100644 --- a/samples/sample17.cpp +++ b/samples/sample17.cpp @@ -8,11 +8,10 @@ using builder::dyn_var; using builder::static_var; -const char *bf_program; -dyn_var *print_value_ptr; -dyn_var *get_value_ptr; +dyn_var print_value = builder::with_name("print_value"); +dyn_var get_value = builder::with_name("get_value"); -static int find_matching_closing(int pc) { +static int find_matching_closing(const char* bf_program, int pc) { int count = 1; while (bf_program[pc] != 0 && count > 0) { pc++; @@ -23,7 +22,7 @@ static int find_matching_closing(int pc) { } return pc; } -static int find_matching_opening(int pc) { +static int find_matching_opening(const char* bf_program, int pc) { int count = 1; while (pc >= 0 && count > 0) { pc--; @@ -35,9 +34,7 @@ static int find_matching_opening(int pc) { return pc; } // BF interpreter -static void interpret_bf(void) { - auto &get_value = *get_value_ptr; - auto &print_value = *print_value_ptr; +static void interpret_bf(dyn_var argc, dyn_var argv, const char* bf_program) { dyn_var pointer = 0; static_var pc = 0; @@ -56,12 +53,12 @@ static void interpret_bf(void) { } else if (bf_program[pc] == ',') { tape[pointer] = get_value(); } else if (bf_program[pc] == '[') { - int closing = find_matching_closing(pc); + int closing = find_matching_closing(bf_program, pc); if (tape[pointer] == 0) { pc = closing; } } else if (bf_program[pc] == ']') { - int opening = find_matching_opening(pc); + int opening = find_matching_opening(bf_program, pc); pc = opening - 1; } pc += 1; @@ -72,19 +69,15 @@ static void print_wrapper_code(std::ostream &oss) { oss << "#include \n"; oss << "void print_value(int x) {printf(\"%c\", x);}\n"; oss << "int get_value(void) {char x; scanf(\" %c\", &x); return x;}\n"; - oss << "int main(int argc, char* argv[]) "; } int main(int argc, char *argv[]) { builder::builder_context context; // BF program that prints hello world - bf_program = "++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.++++++" + const char* bf_program = "++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.++++++" "+..+++.>>.<-.<.+++.------.--------.>>+.>++."; - print_value_ptr = context.assume_variable>("print_value"); - get_value_ptr = context.assume_variable>("get_value"); - - auto ast = context.extract_ast_from_function(interpret_bf); + auto ast = context.extract_function_ast(interpret_bf, "main", bf_program); print_wrapper_code(std::cout); block::c_code_generator::generate_code(ast, std::cout, 0); diff --git a/samples/sample19.cpp b/samples/sample19.cpp index 80aa86e..1185dff 100644 --- a/samples/sample19.cpp +++ b/samples/sample19.cpp @@ -8,7 +8,7 @@ using builder::dyn_var; // A simple straight line code that uses lambda instead of function int main(int argc, char *argv[]) { builder::builder_context context; - auto ast = context.extract_ast_from_lambda([=](void) { + auto ast = context.extract_ast_from_function([=](void) { dyn_var a; dyn_var b; dyn_var c; diff --git a/samples/sample21.cpp b/samples/sample21.cpp index 72fa458..4c7d7fc 100644 --- a/samples/sample21.cpp +++ b/samples/sample21.cpp @@ -7,6 +7,9 @@ using builder::dyn_var; using builder::static_var; + + +// We are now deprecating foreign_exprs class dummy { public: std::string value; @@ -19,11 +22,7 @@ class dummy { return false; } - operator builder::builder() const { - builder::builder b; - b.construct_builder_from_foreign_expr(*this); - return b; - } + // Operator builder::builder is not defined anymore }; // A simple straight line code with 2 variable declarations and one operator @@ -33,9 +32,10 @@ int main(int argc, char *argv[]) { dummy foo; foo.value = "foo"; - auto ast = context.extract_ast_from_lambda([=] { + auto ast = context.extract_ast_from_function([=] { dyn_var x = 2; - dyn_var bar = foo + x; + // Since foreign functions are deprecated, use the value from foo + dyn_var bar = foo.value + x; }); ast->dump(std::cout, 0); diff --git a/samples/sample22.cpp b/samples/sample22.cpp index b082d85..2da2d8f 100644 --- a/samples/sample22.cpp +++ b/samples/sample22.cpp @@ -10,7 +10,7 @@ using builder::static_var; int main(int argc, char *argv[]) { builder::builder_context context; // Code to test assignments between static var and normal vars - auto ast = context.extract_ast_from_lambda([=](void) { + auto ast = context.extract_ast_from_function([=](void) { static_var foo = 4 + 5; dyn_var x = foo + 9; dyn_var y = 8 + foo; diff --git a/samples/sample25.cpp b/samples/sample25.cpp index 4f67d1d..7656877 100644 --- a/samples/sample25.cpp +++ b/samples/sample25.cpp @@ -17,7 +17,7 @@ static void bar(dyn_var x) { x = b + 1; } -static dyn_var foobar(dyn_var x, int &iter, dyn_var &print_val) { +static dyn_var foobar(dyn_var x, const int &iter, dyn_var &print_val) { dyn_var t = 0; for (static_var i = 0; i < iter; i++) t = t + x + 1; @@ -44,7 +44,7 @@ int main(int argc, char *argv[]) { ast3->dump(std::cout, 0); block::c_code_generator::generate_code(ast3, std::cout, 0); - auto &print_val = *(context.assume_variable>("print_val")); + dyn_var print_val = builder::with_name("print_val"); auto ast4 = context.extract_function_ast(foobar, "func4", 2, print_val); ast4->dump(std::cout, 0); block::c_code_generator::generate_code(ast4, std::cout, 0); diff --git a/samples/sample31.cpp b/samples/sample31.cpp index 25cc432..b46dd01 100644 --- a/samples/sample31.cpp +++ b/samples/sample31.cpp @@ -8,19 +8,21 @@ using builder::as_member; using builder::dyn_var; // We will now create our own dyn_var type with members +// We are deprecating extending dyn_var with members +// instead one can write a specialization for a particular type + +struct foo { + static constexpr const char* type_name = "foo"; +}; + template -struct my_dyn_var : public dyn_var { - using dyn_var::dyn_var; - using dyn_var::operator=; +struct my_dyn_var: public builder::dyn_var { + using builder::dyn_var::dyn_var; + using builder::dyn_var::operator=; - my_dyn_var(const my_dyn_var &t) : dyn_var((builder::builder)t) {} - template - my_dyn_var(const my_dyn_var &t) : dyn_var((builder::builder)t) {} - template - builder::builder operator=(const my_dyn_var &t) { - return (*this) = (builder::builder)t; - } - builder::builder operator=(const my_dyn_var &t) { + my_dyn_var(const my_dyn_var &t) : builder::dyn_var((builder::builder)t) {} + + builder::builder operator=(const my_dyn_var &t) { return (*this) = (builder::builder)t; } dyn_var var1 = as_member(this, "var1"); @@ -30,8 +32,8 @@ struct my_dyn_var : public dyn_var { int main(int argc, char *argv[]) { builder::builder_context context; auto ast = context.extract_function_ast( - [=](my_dyn_var x) -> my_dyn_var { - my_dyn_var z = 3 + x.var1 + 5 * x.var1; + [=](my_dyn_var x) -> dyn_var { + my_dyn_var z = 3 + x.var1 + 5 * x.var1; return z.neighbor + 2; }, "func1"); diff --git a/samples/sample61.cpp b/samples/sample61.cpp index 70e41e5..6da32ed 100644 --- a/samples/sample61.cpp +++ b/samples/sample61.cpp @@ -11,16 +11,16 @@ using builder::dyn_var; using builder::static_var; using builder::nd_var; - static void bar(void) { - nd_var t; - dyn_var x = (bool)t; - t.require_equal(true); + nd_var t; + dyn_var x = ((builder::true_top)t).value; + + t.require_val(builder::true_top::T); - nd_var r(true); - dyn_var y = (bool)r; - r.require_equal(false); - t.require_equal(true); + nd_var r(builder::true_top::F); + dyn_var y = r.get()->value; + r.require_val(builder::true_top::F); + t.require_val(builder::true_top::T); } int main(int argc, char* argv[]) { diff --git a/samples/sample64.cpp b/samples/sample64.cpp index 699e004..39a4dd5 100644 --- a/samples/sample64.cpp +++ b/samples/sample64.cpp @@ -9,11 +9,12 @@ using builder::dyn_var; using builder::static_var; -static dyn_var power(dyn_var base_in, dyn_var exp_in) { +static dyn_var power(dyn_var base, dyn_var exp) { + + // Make a copy of exponent because we want to make sure it isn't + // copy eliminated partially - // Create copies so that test cases work well - // MAC os has descripancy in the order arguments are copied in - dyn_var base = base_in; dyn_var exponent = exp_in; + dyn_var exponent = exp; dyn_var res = 1, x = base; while (exponent > 1) { diff --git a/samples/sample66.cpp b/samples/sample66.cpp new file mode 100644 index 0000000..12cf7f3 --- /dev/null +++ b/samples/sample66.cpp @@ -0,0 +1,27 @@ +// Include the headers +#include "blocks/c_code_generator.h" +#include "builder/static_var.h" +#include "builder/dyn_var.h" +#include "blocks/rce.h" +#include + +// Include the BuildIt types +using builder::dyn_var; +using builder::static_var; + +static void bar(std::vector x) { + dyn_var y = x; + for (dyn_var i = 0; i < x.size(); ++i) { + y[i] = y[i] + y[i-1]; + } +} + +int main(int argc, char* argv[]) { + builder::builder_context context; + std::vector x = {1, 2, 3}; + auto ast = context.extract_function_ast(bar, "bar", x); + block::c_code_generator::generate_code(ast, std::cout, 0); + return 0; +} + + diff --git a/src/blocks/annotation_finder.cpp b/src/blocks/annotation_finder.cpp index ed7619b..a62cb6f 100644 --- a/src/blocks/annotation_finder.cpp +++ b/src/blocks/annotation_finder.cpp @@ -2,17 +2,17 @@ namespace block { void annotation_finder::visit(expr_stmt::Ptr stmt) { - if (annotation_to_find == stmt->annotation) { + if (stmt->annotation.find(annotation_to_find) != stmt->annotation.end()) { found_stmt = stmt; } } void annotation_finder::visit(decl_stmt::Ptr stmt) { - if (annotation_to_find == stmt->annotation) { + if (stmt->annotation.find(annotation_to_find) != stmt->annotation.end()) { found_stmt = stmt; } } void annotation_finder::visit(if_stmt::Ptr stmt) { - if (annotation_to_find == stmt->annotation) { + if (stmt->annotation.find(annotation_to_find) != stmt->annotation.end()) { found_stmt = stmt; return; } @@ -20,14 +20,14 @@ void annotation_finder::visit(if_stmt::Ptr stmt) { stmt->else_stmt->accept(this); } void annotation_finder::visit(while_stmt::Ptr stmt) { - if (annotation_to_find == stmt->annotation) { + if (stmt->annotation.find(annotation_to_find) != stmt->annotation.end()) { found_stmt = stmt; return; } stmt->body->accept(this); } void annotation_finder::visit(for_stmt::Ptr stmt) { - if (annotation_to_find == stmt->annotation) { + if (stmt->annotation.find(annotation_to_find) != stmt->annotation.end()) { found_stmt = stmt; return; } diff --git a/src/blocks/c_code_generator.cpp b/src/blocks/c_code_generator.cpp index 1d0e522..61cc646 100644 --- a/src/blocks/c_code_generator.cpp +++ b/src/blocks/c_code_generator.cpp @@ -174,8 +174,11 @@ void c_code_generator::visit(assign_expr::Ptr a) { void c_code_generator::visit(expr_stmt::Ptr a) { a->expr1->accept(this); oss << ";"; - if (a->annotation != "") - oss << " //" << a->annotation; + if (!a->annotation.empty()) { + oss << " //"; + for (auto s: a->annotation) + oss << s << " "; + } } void c_code_generator::visit(stmt_block::Ptr a) { oss << "{"; @@ -192,178 +195,161 @@ void c_code_generator::visit(stmt_block::Ptr a) { oss << "}"; } -void c_code_generator::visit(scalar_type::Ptr type) { - switch (type->scalar_type_id) { - case scalar_type::SHORT_INT_TYPE: - oss << "short int"; - break; - case scalar_type::UNSIGNED_SHORT_INT_TYPE: - oss << "unsigned short int"; - break; - case scalar_type::INT_TYPE: - oss << "int"; - break; - case scalar_type::UNSIGNED_INT_TYPE: - oss << "unsigned int"; - break; - case scalar_type::LONG_INT_TYPE: - oss << "long int"; - break; - case scalar_type::UNSIGNED_LONG_INT_TYPE: - oss << "unsigned long int"; - break; - case scalar_type::LONG_LONG_INT_TYPE: - oss << "long long int"; - break; - case scalar_type::UNSIGNED_LONG_LONG_INT_TYPE: - oss << "unsigned long long int"; - break; - case scalar_type::CHAR_TYPE: - oss << "char"; - break; - case scalar_type::UNSIGNED_CHAR_TYPE: - oss << "unsigned char"; - break; - case scalar_type::VOID_TYPE: - oss << "void"; - break; - case scalar_type::FLOAT_TYPE: - oss << "float"; - break; - case scalar_type::DOUBLE_TYPE: - oss << "double"; - break; - case scalar_type::BOOL_TYPE: - oss << "bool"; - break; - default: - assert(false && "Invalid scalar type"); + + +static std::string addQualifiers(type::Ptr a, bool before = true) { + std::string ret = ""; + if (before) { + if (a->is_const) ret += "const "; + if (a->is_volatile) ret += "volatile "; + } else { + if (a->is_const) ret += " const"; + if (a->is_volatile) ret += " volatile"; } - if (type->is_const) oss << " const"; - if (type->is_volatile) oss << " volatile"; -} -void c_code_generator::visit(named_type::Ptr type) { - oss << type->type_name; - if (type->template_args.size()) { - oss << "<"; - bool needs_comma = false; - for (auto a : type->template_args) { - if (needs_comma) - oss << ", "; - needs_comma = true; - a->accept(this); + return ret; +} +static std::string complex_type_helper(type::Ptr a, std::string name) { + if (isa(a)) { + auto ptr = to(a); + std::string decorated = "*" + addQualifiers(ptr, false) + name; + if (isa(ptr->pointee_type) || isa(ptr->pointee_type)) { + decorated = " (" + decorated + ")"; + } + return complex_type_helper(ptr->pointee_type, decorated); + } else if (isa(a)) { + auto arr = to(a); + std::string ret = name + "["; + if (arr->size != -1) + ret += std::to_string(arr->size); + ret += "]"; + return complex_type_helper(arr->element_type, ret); + } else if (isa(a)) { + auto ft = to(a); + std::string ret = name + "("; + for (unsigned int i = 0; i < ft->arg_types.size(); i++) { + ret += complex_type_helper(ft->arg_types[i], ""); + if (i + 1 < ft->arg_types.size()) + ret += ", "; + } + ret += ")"; + return complex_type_helper(ft->return_type, ret); + } else if (isa(a)) { + auto st = to(a); + std::string tp; + switch (st->scalar_type_id) { + case scalar_type::SHORT_INT_TYPE: + tp = "short int"; + break; + case scalar_type::UNSIGNED_SHORT_INT_TYPE: + tp = "unsigned short int"; + break; + case scalar_type::INT_TYPE: + tp = "int"; + break; + case scalar_type::UNSIGNED_INT_TYPE: + tp = "unsigned int"; + break; + case scalar_type::LONG_INT_TYPE: + tp = "long int"; + break; + case scalar_type::UNSIGNED_LONG_INT_TYPE: + tp = "unsigned long int"; + break; + case scalar_type::LONG_LONG_INT_TYPE: + tp = "long long int"; + break; + case scalar_type::UNSIGNED_LONG_LONG_INT_TYPE: + tp = "unsigned long long int"; + break; + case scalar_type::CHAR_TYPE: + tp = "char"; + break; + case scalar_type::UNSIGNED_CHAR_TYPE: + tp = "unsigned char"; + break; + case scalar_type::VOID_TYPE: + tp = "void"; + break; + case scalar_type::FLOAT_TYPE: + tp = "float"; + break; + case scalar_type::DOUBLE_TYPE: + tp = "double"; + break; + case scalar_type::BOOL_TYPE: + tp = "bool"; + break; + default: + assert(false && "Invalid scalar type"); } - oss << ">"; + return addQualifiers(st) + tp + name; + } else if (isa(a)) { + auto nt = to(a); + std::string ret = addQualifiers(nt) + nt->type_name; + if (nt->template_args.size()) { + ret += "<"; + bool needs_comma = false; + for (auto a : nt->template_args) { + if (needs_comma) + ret += ", "; + needs_comma = true; + ret += complex_type_helper(a, ""); + } + ret += ">"; + } + return ret + name; + } else if (isa(a)) { + auto ptr = to(a); + std::string decorated = "&" + addQualifiers(ptr, false) + name; + if (isa(ptr->referenced_type) || isa(ptr->referenced_type)) { + decorated = " (" + decorated + ")"; + } + return complex_type_helper(ptr->referenced_type, decorated); + } else if (isa(a)) { + auto bt = to(a); + std::string ret = addQualifiers(bt); + if (bt->builder_var_type_id == builder_var_type::DYN_VAR) + ret += "builder::dyn_var<"; + else if (bt->builder_var_type_id == builder_var_type::STATIC_VAR) + ret += "builder::static_var<"; + ret += complex_type_helper(bt->closure_type, ""); + ret += ">"; + return ret + name; } - if (type->is_const) oss << " const"; - if (type->is_volatile) oss << " volatile"; + a->dump(std::cerr, 0); + assert(false && "Invalid type"); +} + +void c_code_generator::visit(scalar_type::Ptr type) { + oss << complex_type_helper(type, ""); +} +void c_code_generator::visit(named_type::Ptr type) { + oss << complex_type_helper(type, ""); } void c_code_generator::visit(pointer_type::Ptr type) { - if (!isa(type->pointee_type) && !isa(type->pointee_type) && - !isa(type->pointee_type)) - assert(false && "Printing pointers of complex type is not supported yet"); - type->pointee_type->accept(this); - oss << "*"; - if (type->is_const) oss << " const"; - if (type->is_volatile) oss << " volatile"; + oss << complex_type_helper(type, ""); } void c_code_generator::visit(reference_type::Ptr type) { - if (!isa(type->referenced_type) && !isa(type->referenced_type) && - !isa(type->referenced_type)) - assert(false && "Printing pointers of complex type is not supported yet"); - type->referenced_type->accept(this); - oss << "&"; - if (type->is_const) oss << " const"; - if (type->is_volatile) oss << " volatile"; + oss << complex_type_helper(type, ""); } void c_code_generator::visit(array_type::Ptr type) { - if (!isa(type->element_type) && !isa(type->element_type) && - !isa(type->element_type)) - assert(false && "Printing arrays of complex type is not supported yet"); - type->element_type->accept(this); - if (type->size != -1) - oss << "[" << type->size << "]"; - else - oss << "[]"; + oss << complex_type_helper(type, ""); } void c_code_generator::visit(builder_var_type::Ptr type) { - if (type->builder_var_type_id == builder_var_type::DYN_VAR) - oss << "builder::dyn_var<"; - else if (type->builder_var_type_id == builder_var_type::STATIC_VAR) - oss << "builder::static_var<"; - type->closure_type->accept(this); - oss << ">"; + oss << complex_type_helper(type, ""); } void c_code_generator::visit(var::Ptr var) { oss << var->var_name; } -static void print_array_decl(std::ostream &oss, array_type::Ptr atype, var::Ptr decl_var, c_code_generator *self, - std::stringstream &append) { - append << "["; - if (atype->size != -1) - append << atype->size; - append << "]"; - - if (isa(atype->element_type)) - print_array_decl(oss, to(atype->element_type), decl_var, self, append); - else if (isa(atype->element_type) || isa(atype->element_type) || - isa(atype->element_type)) { - atype->element_type->accept(self); - if (decl_var->hasMetadata>("attributes")) { - const auto &attributes = decl_var->getMetadata>("attributes"); - for (auto attr : attributes) { - oss << " " << attr; - } - } - oss << " "; - oss << decl_var->var_name; - oss << append.str(); - } else { - assert(false && "Printing arrays of complex type is not supported yet"); - } -} - void c_code_generator::visit(decl_stmt::Ptr a) { - if (isa(a->decl_var->var_type)) { - function_type::Ptr type = to(a->decl_var->var_type); - type->return_type->accept(this); - oss << " (*"; - oss << a->decl_var->var_name; - oss << ")("; - for (unsigned int i = 0; i < type->arg_types.size(); i++) { - type->arg_types[i]->accept(this); - if (i != type->arg_types.size() - 1) - oss << ", "; - } - oss << ")"; - if (a->init_expr != nullptr) { - oss << " = "; - a->init_expr->accept(this); - } - oss << ";"; - return; - } else if (isa(a->decl_var->var_type)) { - array_type::Ptr type = to(a->decl_var->var_type); - std::stringstream s; - print_array_decl(oss, type, a->decl_var, this, s); - if (a->init_expr != nullptr) { - oss << " = "; - a->init_expr->accept(this); - } - oss << ";"; - return; - } - - a->decl_var->var_type->accept(this); + oss << complex_type_helper(a->decl_var->var_type, " " + a->decl_var->var_name); if (a->decl_var->hasMetadata>("attributes")) { const auto &attributes = a->decl_var->getMetadata>("attributes"); for (auto attr : attributes) { oss << " " << attr; } } - oss << " "; - oss << a->decl_var->var_name; if (a->init_expr == nullptr) { oss << ";"; } else { @@ -371,7 +357,6 @@ void c_code_generator::visit(decl_stmt::Ptr a) { a->init_expr->accept(this); oss << ";"; } - // if (a->decl_var->hasMetadata("escapes_static_scope")) oss << " //" << "escapes_static_scope = 1"; } void c_code_generator::visit(if_stmt::Ptr a) { oss << "if ("; @@ -526,12 +511,7 @@ void c_code_generator::visit(func_decl::Ptr a) { if (printDelim) oss << ", "; printDelim = true; - if (isa(arg->var_type)) { - handle_func_arg(arg); - } else { - arg->var_type->accept(this); - oss << " " << arg->var_name; - } + oss << complex_type_helper(arg->var_type, " " + arg->var_name); } if (!printDelim) oss << "void"; diff --git a/src/blocks/for_loop_finder.cpp b/src/blocks/for_loop_finder.cpp index 3df0f35..be5c596 100644 --- a/src/blocks/for_loop_finder.cpp +++ b/src/blocks/for_loop_finder.cpp @@ -159,7 +159,7 @@ void for_loop_finder::visit(stmt_block::Ptr a) { } else for_loop->decl_stmt = a->stmts[while_loop_index]; for_loop->annotation = for_loop->decl_stmt->annotation; - for_loop->decl_stmt->annotation = ""; + for_loop->decl_stmt->annotation.clear(); for_loop->cond = loop->cond; if (parents.size() == 0) { for_loop->update = nullptr; diff --git a/src/blocks/loop_roll.cpp b/src/blocks/loop_roll.cpp index 2d5974e..7b06e9f 100644 --- a/src/blocks/loop_roll.cpp +++ b/src/blocks/loop_roll.cpp @@ -2,9 +2,13 @@ #include "builder/builder.h" #include "builder/dyn_var.h" namespace block { -static bool is_roll(std::string s) { - if (s != "" && s.length() > 5 && s[0] == 'r' && s[1] == 'o' && s[2] == 'l' && s[3] == 'l' && s[4] == '.') - return true; +static bool is_roll(std::set ss, std::string &match) { + for (auto s: ss) { + if (s != "" && s.length() > 5 && s[0] == 'r' && s[1] == 'o' && s[2] == 'l' && s[3] == 'l' && s[4] == '.') { + match = s; + return true; + } + } return false; } static int unique_counter = 0; @@ -141,7 +145,8 @@ static void process_match(stmt_block::Ptr b, int match_start, int match_end) { first->accept(&replacer); to(new_for->body)->stmts.push_back(first); - first->annotation = "from." + first->annotation; + + //first->annotation = "from." + first->annotation; new_stmts.push_back(new_for); @@ -160,12 +165,12 @@ void loop_roll_finder::visit(stmt_block::Ptr b) { int match_end = -1; for (unsigned int i = 0; i < b->stmts.size(); i++) { auto stmt = b->stmts[i]; - if (is_roll(stmt->annotation)) { - std::string match = stmt->annotation; + std::string match; + if (is_roll(stmt->annotation, match)) { match_start = i; match_end = b->stmts.size(); for (; i < b->stmts.size(); i++) { - if (b->stmts[i]->annotation != match) { + if (b->stmts[i]->annotation.find(match) == b->stmts[i]->annotation.end()) { match_end = i; break; } diff --git a/src/builder/annotate.cpp b/src/builder/annotate.cpp index 5cecfa0..64fa878 100644 --- a/src/builder/annotate.cpp +++ b/src/builder/annotate.cpp @@ -2,9 +2,9 @@ namespace builder { void annotate(std::string label) { - builder_context::current_builder_context->commit_uncommitted(); - if (builder_context::current_builder_context->bool_vector.size() > 0) + get_run_state()->commit_uncommitted(); + if (get_run_state()->is_catching_up()) return; - builder_context::current_builder_context->current_label = label; + get_run_state()->add_annotation(label); } } // namespace builder diff --git a/src/builder/builder.cpp b/src/builder/builder.cpp index ff9b825..9712f56 100644 --- a/src/builder/builder.cpp +++ b/src/builder/builder.cpp @@ -18,10 +18,10 @@ std::vector extract_type_vector_dyn<>(void) { void create_return_stmt(const builder &a) { - builder_context::current_builder_context->remove_node_from_sequence(a.block_expr); - builder_context::current_builder_context->commit_uncommitted(); + get_run_state()->remove_node_from_sequence(a.block_expr); + get_run_state()->commit_uncommitted(); - if (builder_context::current_builder_context->bool_vector.size() > 0) + if (get_run_state()->is_catching_up()) return; block::return_stmt::Ptr ret_stmt = std::make_shared(); // ret_stmt->static_offset = a.block_expr->static_offset; @@ -30,7 +30,7 @@ void create_return_stmt(const builder &a) { // jump is as bad a return. Also no performance issues ret_stmt->static_offset = tracer::get_unique_tag(); ret_stmt->return_val = a.block_expr; - builder_context::current_builder_context->add_stmt_to_current_block(ret_stmt); + get_run_state()->add_stmt_to_current_block(ret_stmt, true); } builder::builder(const var &a) { if (builder_precheck()) { @@ -39,16 +39,16 @@ builder::builder(const var &a) { } block_expr = nullptr; - tracer::tag offset = get_offset_in_function(); + tracer::tag offset = tracer::get_offset_in_function(); if (a.current_state == var::member_var) { assert(a.parent_var != nullptr); builder parent_expr_builder = (builder)(*a.parent_var); block::member_access_expr::Ptr member = std::make_shared(); member->parent_expr = parent_expr_builder.block_expr; - builder_context::current_builder_context->remove_node_from_sequence(member->parent_expr); + get_run_state()->remove_node_from_sequence(member->parent_expr); member->member_name = a.var_name; - builder_context::current_builder_context->add_node_to_sequence(member); + get_run_state()->add_node_to_sequence(member); block_expr = member; } else if (a.current_state == var::compound_expr) { @@ -65,7 +65,7 @@ builder::builder(const var &a) { var_expr->static_offset = offset; var_expr->var1 = a.block_var; - builder_context::current_builder_context->add_node_to_sequence(var_expr); + get_run_state()->add_node_to_sequence(var_expr); block_expr = var_expr; } diff --git a/src/builder/builder_context.cpp b/src/builder/builder_context.cpp index 1544e99..322da36 100644 --- a/src/builder/builder_context.cpp +++ b/src/builder/builder_context.cpp @@ -15,116 +15,7 @@ #include namespace builder { -builder_context *builder_context::current_builder_context = nullptr; -int builder_context::debug_creation_counter = 0; - -void builder_context::add_stmt_to_current_block(block::stmt::Ptr s, bool check_for_conflicts) { - if (bool_vector.size() > 0) { - return; - } - if (current_label != "") { - s->annotation = current_label; - current_label = ""; - } - if (!s->static_offset.is_empty() && is_visited_tag(s->static_offset) > 0) { - - throw LoopBackException(s->static_offset); - } - tracer::tag stag = s->static_offset; - if (use_memoization && memoized_tags->map.find(stag) != memoized_tags->map.end() && check_for_conflicts && - bool_vector.size() == 0) { - // This tag has been seen on some other execution. We can reuse. - // First find the tag - - - block::stmt_block::Ptr parent = memoized_tags->map[stag]; - unsigned int i = 0; - for (i = 0; i < parent->stmts.size(); i++) { - if (parent->stmts[i]->static_offset == s->static_offset) - break; - } - // Special case of stmt expr and if_stmt - if (block::isa(s) && block::isa(parent->stmts[i])) { - block::if_stmt::Ptr p_stmt = block::to(parent->stmts[i]); - block::expr_stmt::Ptr expr = block::to(s); - - if (p_stmt->cond->is_same(expr->expr1)) - throw MemoizationException(s->static_offset, parent, i); - } - - if (parent->stmts[i]->is_same(s)) - throw MemoizationException(s->static_offset, parent, i); - } - visited_offsets.insert(s->static_offset); - current_block_stmt->stmts.push_back(s); -} -tracer::tag get_offset_in_function(void) { - tracer::tag offset = tracer::get_offset_in_function_impl(builder_context::current_builder_context); - return offset; -} -builder_context::~builder_context() { - for (unsigned int i = 0; i < assume_variables.size(); i++) { - delete assume_variables[i]; - } -} -bool builder_context::is_visited_tag(tracer::tag &new_tag) { - if (visited_offsets.find(new_tag) != visited_offsets.end()) - return true; - return false; -} -void builder_context::erase_tag(tracer::tag &erase_tag) { - visited_offsets.erase(erase_tag); -} -void builder_context::commit_uncommitted(void) { - for (auto block_ptr : uncommitted_sequence) { - block::expr_stmt::Ptr s = std::make_shared(); - assert(block::isa(block_ptr)); - s->static_offset = block_ptr->static_offset; - s->expr1 = block::to(block_ptr); - assert(current_block_stmt != nullptr); - add_stmt_to_current_block(s); - } - uncommitted_sequence.clear(); -} -void builder_context::remove_node_from_sequence(block::expr::Ptr e) { - // At this point, there is a chance a statement _might_ have been committed if - // a variable was declared. This happens when you return a dyn_var from a function - // Now this is not particularly bad because it just leaves a stray expression in the - // generated code, but 1. it can mess with some pattern matchers, 2. could have - // unexpected side effects, so we are going to do a clean up just to be sure - // So we will check if the expr that we are trying to delete is in the uncommitted - // sequence, if not we will try to find for it in the committed expressions - if (std::find(uncommitted_sequence.begin(), uncommitted_sequence.end(), e) != uncommitted_sequence.end()) { - uncommitted_sequence.remove(e); - } else { - // Could be committed already - // It is safe to update the parent block here, because the memoization doesn't care about indices - // But don't actually delete the statement, because there could be gotos that are jumping here - // instead just mark it for deletion later - for (auto stmt : current_block_stmt->stmts) { - if (block::isa(stmt)) { - auto expr_s = block::to(stmt); - if (expr_s->expr1 == e) { - expr_s->mark_for_deletion = true; - } - } - } - } -} -void builder_context::add_node_to_sequence(block::expr::Ptr e) { - uncommitted_sequence.push_back(e); -} - -bool get_next_bool_from_context(builder_context *context, block::expr::Ptr expr) { - context->commit_uncommitted(); - if (context->bool_vector.size() == 0) { - tracer::tag offset = expr->static_offset; - throw OutOfBoolsException(offset); - } - bool ret_val = context->bool_vector.back(); - context->bool_vector.pop_back(); - return ret_val; -} static void trim_ast_at_offset(block::stmt::Ptr ast, tracer::tag offset) { return; @@ -266,12 +157,8 @@ trim_common_from_back(block::stmt::Ptr ast1, block::stmt::Ptr ast2) { std::reverse(trimmed_stmts.begin(), trimmed_stmts.end()); return {trimmed_stmts, split_decls}; } -block::stmt::Ptr builder_context::extract_ast_from_lambda(std::function lambda) { - internal_stored_lambda = lambda; - return extract_ast_from_function_impl(); -} - -void builder_context::reset_for_nd_failure() { +/* +static void builder_context::reset_for_nd_failure() { // Clear all shared_state memoized_tags->map.clear(); // Clear per run state if any @@ -288,25 +175,25 @@ void builder_context::reset_for_nd_failure() { // Increment creation counter since we are running again debug_creation_counter++; } +*/ -block::stmt::Ptr builder_context::extract_ast_from_function_impl(void) { + +void builder_context::extract_function_ast_impl(invocation_state* i_state) { #ifndef ENABLE_D2X if (enable_d2x) assert(false && "D2X support cannot be enabled without the ENABLE_D2X build option"); #endif - - std::vector b; - - nd_state_map = &_nd_state_map; - - block::stmt::Ptr ast; - + block::stmt::Ptr ast = nullptr; + // Repeat till ND vars are happy while (1) { try { - ast = extract_ast_from_function_internal(b); + // Allocate one execution_state for this ND run + execution_state e_state (i_state); + // Allocate one run_state, rest will be allocated by the recursive calls + run_state r_state (&e_state, i_state); + ast = extract_ast_from_run(&r_state); } catch (NonDeterministicFailureException &e) { - reset_for_nd_failure(); continue; } break; @@ -367,69 +254,60 @@ block::stmt::Ptr builder_context::extract_ast_from_function_impl(void) { block::loop_roll_finder loop_roll_finder; ast->accept(&loop_roll_finder); - return ast; -} -block::stmt::Ptr builder_context::extract_ast_from_function_internal(std::vector b) { - current_block_stmt = std::make_shared(); - current_block_stmt->static_offset.clear(); - assert(current_block_stmt != nullptr); - ast = current_block_stmt; + i_state->generated_func_decl->body = ast; +} +block::stmt::Ptr builder_context::extract_ast_from_run(run_state* r_state) { + r_state->current_stmt_block = std::make_shared(); + block::stmt_block::Ptr ast = r_state->current_stmt_block; + // A new run is starting, clear the parent stack + // for identifying nested members. This is because a run can end mid construction if (parents_stack != nullptr) { parents_stack->clear(); } - bool_vector = b; + block::stmt_block::Ptr ret_ast; + + std::vector bool_vector_copy = r_state->bool_vector; - block::stmt::Ptr ret_ast; try { - current_builder_context = this; + run_state::current_run_state = r_state; // function(); - lambda_wrapper(internal_stored_lambda); - commit_uncommitted(); + lambda_wrapper(r_state->i_state->invocation_function); + r_state->commit_uncommitted(); ret_ast = ast; - current_builder_context = nullptr; + run_state::current_run_state = nullptr; } catch (OutOfBoolsException &e) { - current_builder_context = nullptr; + run_state::current_run_state = nullptr; - block::expr_stmt::Ptr last_stmt = block::to(current_block_stmt->stmts.back()); - current_block_stmt->stmts.pop_back(); - // erase_tag(e.static_offset); + block::expr_stmt::Ptr last_stmt = block::to(r_state->current_stmt_block->stmts.back()); + r_state->current_stmt_block->stmts.pop_back(); block::expr::Ptr cond_expr = last_stmt->expr1; - - builder_context true_context(memoized_tags); - true_context.expr_sequence = expr_sequence; - true_context.use_memoization = use_memoization; - true_context.visited_offsets = visited_offsets; - true_context.internal_stored_lambda = internal_stored_lambda; - true_context.feature_unstructured = feature_unstructured; - true_context.enable_d2x = enable_d2x; - true_context.nd_state_map = nd_state_map; - - std::vector true_bv; - true_bv.push_back(true); - std::copy(b.begin(), b.end(), std::back_inserter(true_bv)); - block::stmt_block::Ptr true_ast = - block::to(true_context.extract_ast_from_function_internal(true_bv)); - - builder_context false_context(memoized_tags); - false_context.expr_sequence = std::move(expr_sequence); - false_context.use_memoization = use_memoization; - false_context.visited_offsets = visited_offsets; - false_context.internal_stored_lambda = internal_stored_lambda; - false_context.feature_unstructured = feature_unstructured; - false_context.enable_d2x = enable_d2x; - false_context.nd_state_map = nd_state_map; - - std::vector false_bv; - false_bv.push_back(false); - std::copy(b.begin(), b.end(), std::back_inserter(false_bv)); - block::stmt_block::Ptr false_ast = - block::to(false_context.extract_ast_from_function_internal(false_bv)); + + // Establish two run_states + run_state true_r_state(r_state->e_state, r_state->i_state); + // Only copy over the expr_sequence since it is part of the r_state that + // just terminated + true_r_state.cached_expr_sequence = r_state->cached_expr_sequence; + true_r_state.bool_vector.push_back(true); + true_r_state.visited_offsets = r_state->visited_offsets; + std::copy(bool_vector_copy.begin(), bool_vector_copy.end(), std::back_inserter(true_r_state.bool_vector)); + + // Establish two run_states + run_state false_r_state(r_state->e_state, r_state->i_state); + false_r_state.visited_offsets = r_state->visited_offsets; + // Only copy over the expr_sequence since it is part of the r_state that + // just terminated + false_r_state.cached_expr_sequence = r_state->cached_expr_sequence; + false_r_state.bool_vector = true_r_state.bool_vector; + false_r_state.bool_vector[0] = false; + + block::stmt_block::Ptr true_ast = block::to(extract_ast_from_run(&true_r_state)); + block::stmt_block::Ptr false_ast = block::to(extract_ast_from_run(&false_r_state)); trim_ast_at_offset(true_ast, e.static_offset); trim_ast_at_offset(false_ast, e.static_offset); @@ -440,7 +318,7 @@ block::stmt::Ptr builder_context::extract_ast_from_function_internal(std::vector std::vector trimmed_stmts = trim_pair.first; std::vector split_decls = trim_pair.second; - erase_tag(e.static_offset); + r_state->erase_tag(e.static_offset); block::if_stmt::Ptr new_if_stmt = std::make_shared(); new_if_stmt->annotation = last_stmt->annotation; @@ -451,47 +329,48 @@ block::stmt::Ptr builder_context::extract_ast_from_function_internal(std::vector new_if_stmt->else_stmt = false_ast; for (auto stmt : split_decls) - add_stmt_to_current_block(stmt, false); - add_stmt_to_current_block(new_if_stmt, false); + r_state->add_stmt_to_current_block(stmt, false); + r_state->add_stmt_to_current_block(new_if_stmt, false); - std::copy(trimmed_stmts.begin(), trimmed_stmts.end(), std::back_inserter(current_block_stmt->stmts)); + std::copy(trimmed_stmts.begin(), trimmed_stmts.end(), std::back_inserter(r_state->current_stmt_block->stmts)); ret_ast = ast; } catch (LoopBackException &e) { - current_builder_context = nullptr; + run_state::current_run_state = nullptr; block::goto_stmt::Ptr goto_stmt = std::make_shared(); goto_stmt->static_offset.clear(); goto_stmt->temporary_label_number = e.static_offset; - add_stmt_to_current_block(goto_stmt, false); + r_state->add_stmt_to_current_block(goto_stmt, false); ret_ast = ast; } catch (MemoizationException &e) { + run_state::current_run_state = nullptr; if (feature_unstructured) { // Instead of copying statements to the current block, we will just insert a goto block::goto_stmt::Ptr goto_stmt = std::make_shared(); goto_stmt->static_offset.clear(); goto_stmt->temporary_label_number = e.static_offset; - add_stmt_to_current_block(goto_stmt, false); + r_state->add_stmt_to_current_block(goto_stmt, false); } else { for (unsigned int i = e.child_id; i < e.parent->stmts.size(); i++) { if (block::isa(e.parent->stmts[i])) { block::goto_stmt::Ptr goto_stmt = std::make_shared(); goto_stmt->static_offset.clear(); goto_stmt->temporary_label_number = block::to(e.parent->stmts[i])->temporary_label_number; - add_stmt_to_current_block(goto_stmt, false); + r_state->add_stmt_to_current_block(goto_stmt, false); } else { - add_stmt_to_current_block(e.parent->stmts[i], false); + r_state->add_stmt_to_current_block(e.parent->stmts[i], false); } } } ret_ast = ast; } - current_builder_context = nullptr; + run_state::current_run_state = nullptr; // Update the memoized table with the stmt block we just created - for (unsigned int i = 0; i < current_block_stmt->stmts.size(); i++) { - block::stmt::Ptr s = current_block_stmt->stmts[i]; + for (unsigned int i = 0; i < r_state->current_stmt_block->stmts.size(); i++) { + block::stmt::Ptr s = r_state->current_stmt_block->stmts[i]; // If any of the statements are if conditions, remove the // internal statements from the table // This is required because of the way we do memoization. @@ -507,29 +386,29 @@ block::stmt::Ptr builder_context::extract_ast_from_function_internal(std::vector assert(block::isa(if1->then_stmt)); assert(block::isa(if1->else_stmt)); for (auto &stmt : block::to(if1->then_stmt)->stmts) { - auto it = memoized_tags->map.find(stmt->static_offset); - if (it != memoized_tags->map.end()) - memoized_tags->map.erase(it); + auto it = r_state->e_state->memoized_tags.find(stmt->static_offset); + if (it != r_state->e_state->memoized_tags.end()) + r_state->e_state->memoized_tags.erase(it); if (feature_unstructured) { auto pblock = block::to(if1->then_stmt); - memoized_tags->map[stmt->static_offset] = pblock; + r_state->e_state->memoized_tags[stmt->static_offset] = pblock; } } for (auto &stmt : block::to(if1->else_stmt)->stmts) { - auto it = memoized_tags->map.find(stmt->static_offset); - if (it != memoized_tags->map.end()) - memoized_tags->map.erase(it); + auto it = r_state->e_state->memoized_tags.find(stmt->static_offset); + if (it != r_state->e_state->memoized_tags.end()) + r_state->e_state->memoized_tags.erase(it); if (feature_unstructured) { auto pblock = block::to(if1->else_stmt); - memoized_tags->map[stmt->static_offset] = pblock; + r_state->e_state->memoized_tags[stmt->static_offset] = pblock; } } } - memoized_tags->map[s->static_offset] = current_block_stmt; + r_state->e_state->memoized_tags[s->static_offset] = r_state->current_stmt_block; } - ast = current_block_stmt = nullptr; + ast = r_state->current_stmt_block = nullptr; return ret_ast; } diff --git a/src/builder/run_states.cpp b/src/builder/run_states.cpp new file mode 100644 index 0000000..91a84c9 --- /dev/null +++ b/src/builder/run_states.cpp @@ -0,0 +1,115 @@ +#include "builder/run_states.h" +#include "builder/exceptions.h" +#include +namespace builder { + +run_state* run_state::current_run_state = nullptr; + +void run_state::add_stmt_to_current_block(block::stmt::Ptr s, bool check_for_conflicts) { + if (bool_vector.size() > 0) { + return; + } + if (!current_annotations.empty()) { + s->annotation = get_and_clear_annotations(); + } + if (!s->static_offset.is_empty() && is_visited_tag(s->static_offset) > 0) { + throw LoopBackException(s->static_offset); + } + + tracer::tag stag = s->static_offset; + + if (e_state->memoized_tags.find(stag) != e_state->memoized_tags.end() && check_for_conflicts && + bool_vector.size() == 0) { + // This tag has been seen on some other execution. We can reuse. + // First find the tag - + block::stmt_block::Ptr parent = e_state->memoized_tags[stag]; + unsigned int i = 0; + for (i = 0; i < parent->stmts.size(); i++) { + if (parent->stmts[i]->static_offset == s->static_offset) + break; + } + // Special case of stmt expr and if_stmt + if (block::isa(s) && block::isa(parent->stmts[i])) { + block::if_stmt::Ptr p_stmt = block::to(parent->stmts[i]); + block::expr_stmt::Ptr expr = block::to(s); + + if (p_stmt->cond->is_same(expr->expr1)) + throw MemoizationException(s->static_offset, parent, i); + } + + if (parent->stmts[i]->is_same(s)) + throw MemoizationException(s->static_offset, parent, i); + } + visited_offsets.insert(s->static_offset); + current_stmt_block->stmts.push_back(s); +} + +bool run_state::is_visited_tag(tracer::tag &new_tag) { + if (visited_offsets.find(new_tag) != visited_offsets.end()) + return true; + return false; +} + +void run_state::erase_tag(tracer::tag &erase_tag) { + visited_offsets.erase(erase_tag); +} +void run_state::commit_uncommitted(void) { + for (auto block_ptr : uncommitted_sequence) { + // if it has been removed by setting it to nullptr, skip + if (block_ptr == nullptr) continue; + block::expr_stmt::Ptr s = std::make_shared(); + assert(block::isa(block_ptr)); + s->static_offset = block_ptr->static_offset; + s->expr1 = block::to(block_ptr); + assert(current_stmt_block != nullptr); + add_stmt_to_current_block(s, true); + } + uncommitted_sequence.clear(); +} +void run_state::remove_node_from_sequence(block::expr::Ptr e) { + // At this point, there is a chance a statement _might_ have been committed if + // a variable was declared. This happens when you return a dyn_var from a function + // Now this is not particularly bad because it just leaves a stray expression in the + // generated code, but 1. it can mess with some pattern matchers, 2. could have + // unexpected side effects, so we are going to do a clean up just to be sure + // So we will check if the expr that we are trying to delete is in the uncommitted + // sequence, if not we will try to find for it in the committed expressions + + bool found = false; + for (unsigned i = 0; i < uncommitted_sequence.size(); i++) { + if (uncommitted_sequence[i] == e) { + uncommitted_sequence[i] = nullptr; + found = true; + } + } + if (!found) { + // Could be committed already + // It is safe to update the parent block here, because the memoization doesn't care about indices + // But don't actually delete the statement, because there could be gotos that are jumping here + // instead just mark it for deletion later + for (auto stmt : current_stmt_block->stmts) { + if (block::isa(stmt)) { + auto expr_s = block::to(stmt); + if (expr_s->expr1 == e) { + expr_s->mark_for_deletion = true; + } + } + } + } +} +void run_state::add_node_to_sequence(block::expr::Ptr e) { + uncommitted_sequence.push_back(e); +} + +bool run_state::get_next_bool(block::expr::Ptr expr) { + commit_uncommitted(); + if (bool_vector.size() == 0) { + tracer::tag offset = expr->static_offset; + throw OutOfBoolsException(offset); + } + bool ret_val = bool_vector.back(); + bool_vector.pop_back(); + return ret_val; +} + +} diff --git a/src/util/tracer.cpp b/src/util/tracer.cpp index 396e26d..a63d629 100644 --- a/src/util/tracer.cpp +++ b/src/util/tracer.cpp @@ -16,7 +16,7 @@ extern void lambda_wrapper_close(void); namespace tracer { #ifdef TRACER_USE_LIBUNWIND -tag get_offset_in_function_impl(builder::builder_context *current_builder_context) { +tag get_offset_in_function(void) { unsigned long long function = (unsigned long long)(void *)builder::lambda_wrapper; unsigned long long function_end = (unsigned long long)(void *)builder::lambda_wrapper_close; unw_context_t context; @@ -33,26 +33,25 @@ tag get_offset_in_function_impl(builder::builder_context *current_builder_contex break; new_tag.pointers.push_back((unsigned long long)ip); } - // Now add snapshots of static vars - assert(current_builder_context != nullptr); - for (auto tuple : current_builder_context->deferred_static_var_tuples) { + // Now add snapshots of static vars + for (auto tuple : builder::get_run_state()->deferred_static_var_tuples) { if (tuple == nullptr) { new_tag.static_var_snapshots.push_back(nullptr); continue; } new_tag.static_var_snapshots.push_back(tuple->snapshot()); - if (builder::builder_context::current_builder_context->enable_d2x) { + if (get_builder_context()->enable_d2x) { new_tag.static_var_key_values.push_back({tuple->var_name, tuple->serialize()}); } } - for (auto tuple : current_builder_context->static_var_tuples) { + for (auto tuple : builder::get_run_state()->static_var_tuples) { if (tuple == nullptr) { new_tag.static_var_snapshots.push_back(nullptr); continue; } new_tag.static_var_snapshots.push_back(tuple->snapshot()); - if (builder::builder_context::current_builder_context->enable_d2x) { + if (get_builder_context()->enable_d2x) { new_tag.static_var_key_values.push_back({tuple->var_name, tuple->serialize()}); } } @@ -60,7 +59,7 @@ tag get_offset_in_function_impl(builder::builder_context *current_builder_contex } #else -tag get_offset_in_function_impl(builder::builder_context *current_builder_context) { +tag get_offset_in_function(void) { unsigned long long function = (unsigned long long)(void *)builder::lambda_wrapper; unsigned long long function_end = (unsigned long long)(void *)builder::lambda_wrapper_close; @@ -75,25 +74,24 @@ tag get_offset_in_function_impl(builder::builder_context *current_builder_contex } // Now add snapshots of static vars - assert(current_builder_context != nullptr); - for (auto tuple : current_builder_context->deferred_static_var_tuples) { + for (auto tuple : builder::get_run_state()->deferred_static_var_tuples) { if (tuple == nullptr) { new_tag.static_var_snapshots.push_back(nullptr); continue; } new_tag.static_var_snapshots.push_back(tuple->snapshot()); - if (builder::builder_context::current_builder_context->enable_d2x) { + if (builder::get_builder_context()->enable_d2x) { new_tag.static_var_key_values.push_back({tuple->var_name, tuple->serialize()}); } } - for (auto tuple : current_builder_context->static_var_tuples) { + for (auto tuple : builder::get_run_state()->static_var_tuples) { if (tuple == nullptr) { new_tag.static_var_snapshots.push_back(nullptr); continue; } new_tag.static_var_snapshots.push_back(tuple->snapshot()); - if (builder::builder_context::current_builder_context->enable_d2x) { + if (builder::get_builder_context()->enable_d2x) { new_tag.static_var_key_values.push_back({tuple->var_name, tuple->serialize()}); } }