diff --git a/include/vast/CodeGen/CodeGenAttrVisitor.hpp b/include/vast/CodeGen/CodeGenAttrVisitor.hpp index 25a8c88bd3..3d11d7ff78 100644 --- a/include/vast/CodeGen/CodeGenAttrVisitor.hpp +++ b/include/vast/CodeGen/CodeGenAttrVisitor.hpp @@ -55,6 +55,10 @@ namespace vast::cg { return make< hl::AnnotationAttr >(attr->getAnnotation()); } + mlir_attr VisitAlwaysInlineAttr(const clang::AlwaysInlineAttr *attr) { + return make< hl::AlwaysInlineAttr >(); + } + mlir_attr VisitLoaderUninitializedAttr(const clang::LoaderUninitializedAttr *attr) { return make< hl::LoaderUninitializedAttr >(); } diff --git a/include/vast/Dialect/HighLevel/HighLevelAttributes.td b/include/vast/Dialect/HighLevel/HighLevelAttributes.td index 3c0d350845..10129abbb8 100644 --- a/include/vast/Dialect/HighLevel/HighLevelAttributes.td +++ b/include/vast/Dialect/HighLevel/HighLevelAttributes.td @@ -33,6 +33,7 @@ class NameAttr def AnnotationAttr : NameAttr< "Annotation", "annotation" >; def FormatAttr : NameAttr< "Format", "format" >; def SectionAttr : NameAttr< "Section", "section" >; +def AlwaysInlineAttr : HighLevel_Attr< "AlwaysInline", "always_inline" >; def ConstAttr : HighLevel_Attr< "Const", "const" >; def LoaderUninitAttr : HighLevel_Attr< "LoaderUninitialized", "loader_uninitialized" >; def NoInstrumentAttr : HighLevel_Attr< "NoInstrumentFunction", "no_instrument_function" >; diff --git a/include/vast/Dialect/HighLevel/HighLevelOps.td b/include/vast/Dialect/HighLevel/HighLevelOps.td index ed47d16961..4833800278 100644 --- a/include/vast/Dialect/HighLevel/HighLevelOps.td +++ b/include/vast/Dialect/HighLevel/HighLevelOps.td @@ -71,6 +71,10 @@ def TypeDeclOp let summary = "VAST type declaration"; let description = [{ VAST type declaration }]; + let extraClassDeclaration = [{ + mlir_type getDefinedType(); + }]; + let assemblyFormat = [{ $name attr-dict }]; } @@ -85,6 +89,10 @@ def TypeDefOp be referenced as NamedType later. }]; + let extraClassDeclaration = [{ + mlir_type getDefinedType(); + }]; + let assemblyFormat = [{ $name attr-dict `:` $type }]; } @@ -197,7 +205,7 @@ def EnumDeclOp let assemblyFormat = [{ $name attr-dict `:` ($type^ $constants)? }]; } -class RecordLikeDeclOp< string mnemonic, string concrete_name, list< Trait > traits = [] > +class RecordLikeDeclOp< string mnemonic, list< Trait > traits = [] > : HighLevel_Op< mnemonic, !listconcat(traits, [NoTerminator, VastSymbol, @@ -218,31 +226,34 @@ class RecordLikeDeclOp< string mnemonic, string concrete_name, list< Trait > tra let extraClassDefinition = [{ // AggregateTypeDefinitionInterface - gap::generator }] # concrete_name # [{ ::getFieldTypes() { + gap::generator< mlir_type > $cppClass::getFieldTypes() { return hl::get_field_types(*this); } - gap::generator>}] # concrete_name # [{ ::getFieldsInfo() { + gap::generator< vast::field_info_t > $cppClass::getFieldsInfo() { return hl::get_fields_info(*this); } - gap::generator< vast::AggregateTypeDefinitionInterface > }] # concrete_name # [{ ::getNestedDeclarations() { + gap::generator< vast::AggregateTypeDefinitionInterface > $cppClass::getNestedDeclarations() { return hl::get_nested_declarations(*this); } - std::string }] # concrete_name # [{ ::getDefinedName() { return this->getName().str(); } + llvm::StringRef $cppClass::getDefinedName() { return this->getName(); } + mlir::Type $cppClass::getDefinedType() { + return hl::RecordType::get(getContext(), getDefinedName()); + } }]; let assemblyFormat = [{ $name attr-dict `:` $fields }]; } -def StructDeclOp : RecordLikeDeclOp< "struct", "StructDeclOp" > { +def StructDeclOp : RecordLikeDeclOp< "struct" > { let summary = "VAST struct declaration"; let description = [{ VAST struct declaration }]; } -def UnionDeclOp : RecordLikeDeclOp< "union", "UnionDeclOp" > { +def UnionDeclOp : RecordLikeDeclOp< "union" > { let summary = "VAST record declaration"; let description = [{ VAST record declaration }]; } @@ -254,6 +265,12 @@ def FieldDeclOp let summary = "VAST record field declaration"; let description = [{ VAST record field declaration }]; + let extraClassDeclaration = [{ + vast::AggregateTypeDefinitionInterface getParentAggregate() { + return mlir::dyn_cast< vast::AggregateTypeDefinitionInterface >((*this)->getParentOp()); + } + }]; + let assemblyFormat = [{ $name attr-dict (`bw` $bits^)? `:` $type }]; } diff --git a/include/vast/Dialect/HighLevel/HighLevelUtils.hpp b/include/vast/Dialect/HighLevel/HighLevelUtils.hpp index 9d458d9b12..0c5d5cfdc1 100644 --- a/include/vast/Dialect/HighLevel/HighLevelUtils.hpp +++ b/include/vast/Dialect/HighLevel/HighLevelUtils.hpp @@ -9,36 +9,41 @@ #include "vast/Interfaces/SymbolInterface.hpp" #include "vast/Util/Common.hpp" +#include +#include "vast/Util/TypeUtils.hpp" -#include "gap/core/generator.hpp" +#include + +#include /* Contains common utilities often needed to work with hl dialect. */ namespace vast::hl { + + using aggregate_interface = AggregateTypeDefinitionInterface; + static inline gap::generator< mlir_type > get_field_types(auto op) { - for (auto [_, type] : get_fields_info(op)) { + for (auto &&[_, type] : get_fields_info(op)) { co_yield type; } } - static inline gap::generator< std::tuple< std::string, mlir_type > > get_fields_info(auto op - ) { + gap::generator< field_info_t > get_fields_info(auto op) { for (auto &maybe_field : op.getOps()) { // Definition of nested structure, we ignore not a field. - if (mlir::isa< AggregateTypeDefinitionInterface >(maybe_field)) { + if (mlir::isa< aggregate_interface >(maybe_field)) { continue; } auto field_decl = mlir::dyn_cast< hl::FieldDeclOp >(maybe_field); VAST_ASSERT(field_decl); - co_yield std::make_tuple(field_decl.getName().str(), field_decl.getType()); + co_yield { field_decl.getName().str(), field_decl.getType() }; } } - static inline gap::generator< AggregateTypeDefinitionInterface > - get_nested_declarations(auto op) { + gap::generator< aggregate_interface > get_nested_declarations(auto op) { for (auto &maybe_field : op.getOps()) { - if (auto casted = mlir::dyn_cast< AggregateTypeDefinitionInterface >(maybe_field)) { + if (auto casted = mlir::dyn_cast< aggregate_interface >(maybe_field)) { co_yield casted; } } @@ -47,76 +52,125 @@ namespace vast::hl { // TODO(hl): This is a placeholder that works in our test cases so far. // In general, we will need generic resolution for scoping that // will be used instead of this function. - static inline auto definition_of(mlir::Type t, vast_module module_op) - -> AggregateTypeDefinitionInterface { - auto type_name = hl::name_of_record(t); - VAST_CHECK(type_name, "hl::name_of_record failed with {0}", t); - - AggregateTypeDefinitionInterface out; - ; - auto walker = [&](AggregateTypeDefinitionInterface op) { + aggregate_interface definition_of(mlir_type ty, auto scope) { + auto type_name = hl::name_of_record(ty); + VAST_CHECK(type_name, "hl::name_of_record failed with {0}", ty); + + aggregate_interface out; + auto walker = [&](aggregate_interface op) { if (op.getDefinedName() == type_name) { out = op; - return mlir::WalkResult::interrupt(); + return walk_result::interrupt(); } return mlir::WalkResult::advance(); }; - module_op->walk(walker); + scope->walk(walker); return out; } - static inline auto field_types(mlir::Type t, vast_module module_op) - -> gap::generator< mlir_type > { - auto def = definition_of(t, module_op); - VAST_CHECK(def, "Was not able to fetch definition of type: {0}", t); + gap::generator< mlir_type > field_types(mlir_type ty, auto scope) { + auto def = definition_of(ty, scope); + VAST_CHECK(def, "Was not able to fetch definition of type: {0}", ty); return def.getFieldTypes(); } - static inline hl::ImplicitCastOp - implicit_cast_lvalue_to_rvalue(auto &rewriter, auto loc, auto lvalue_op) { - auto lvalue_type = mlir::dyn_cast< hl::LValueType >(lvalue_op.getType()); - VAST_ASSERT(lvalue_type); + hl::ImplicitCastOp implicit_cast_lvalue_to_rvalue(auto &rewriter, auto loc, auto lvalue_op) { + auto value_type = mlir::dyn_cast< hl::LValueType >(lvalue_op.getType()); + VAST_ASSERT(value_type); return rewriter.template create< hl::ImplicitCastOp >( - loc, lvalue_type.getElementType(), lvalue_op, hl::CastKind::LValueToRValue + loc, value_type.getElementType(), lvalue_op, hl::CastKind::LValueToRValue ); } // Given record `root` emit `hl::RecordMemberOp` for each its member. - static inline auto generate_ptrs_to_record_members(operation root, auto loc, auto &bld) - -> gap::generator< hl::RecordMemberOp > { - auto module_op = root->getParentOfType< vast_module >(); - VAST_ASSERT(module_op); - auto def = definition_of(root->getResultTypes()[0], module_op); + auto generate_ptrs_to_record_members(operation root, auto loc, auto &bld) + -> gap::generator< hl::RecordMemberOp > + { + auto scope = root->getParentOfType< vast_module >(); + VAST_ASSERT(scope); + VAST_ASSERT(root->getNumResults() == 1); + auto def = definition_of(root->getResultTypes()[0], scope); VAST_CHECK(def, "Was not able to fetch definition of type from: {0}", *root); for (const auto &[name, type] : def.getFieldsInfo()) { - VAST_ASSERT(root->getNumResults() == 1); - auto as_val = root->getResult(0); + auto as_val = root->getResult(0); // `hl.member` requires type to be an lvalue. - auto wrap_type = hl::LValueType::get(module_op.getContext(), type); + auto wrap_type = hl::LValueType::get(scope.getContext(), type); co_yield bld.template create< hl::RecordMemberOp >(loc, wrap_type, as_val, name); } } // Given record `root` emit `hl::RecordMemberOp` casted as rvalue for each // its member. - static inline auto generate_values_of_record_members(operation root, auto &bld) - -> gap::generator< hl::ImplicitCastOp > { + auto generate_values_of_record_members(operation root, auto &bld) + -> gap::generator< hl::ImplicitCastOp > + { for (auto member_ptr : generate_ptrs_to_members(root, bld)) { co_yield implicit_cast_lvalue_to_rvalue(bld, member_ptr->getLoc(), member_ptr); } } - static inline std::optional< std::size_t > - field_idx(llvm::StringRef name, AggregateTypeDefinitionInterface decl) { - std::size_t idx = 0; - // `llvm::enumerate` is unhappy when coroutine is passed in. - for (const auto &[field_name, _] : decl.getFieldsInfo()) { - if (field_name == name) { - return { idx }; + namespace detail + { + template + std::vector to_vector(gap::generator &&gen) { + std::vector result; + std::ranges::copy(gen, std::back_inserter(result)); + return result; + } + } // namespace detail + + static inline auto field_index(string_ref name, aggregate_interface agg) + -> std::optional< std::size_t > + { + for (const auto &field : llvm::enumerate(detail::to_vector(agg.getFieldsInfo()))) { + if (field.value().name == name) { + return field.index(); + } + } + + return std::nullopt; + } + + walk_result users(hl::TypeDefOp op, auto scope, auto &&yield) { + return type_users([&](mlir_type ty) { + if (auto td = mlir::dyn_cast< TypedefType >(ty)) + return td.getName() == op.getName(); + return false; + }, scope, std::forward< decltype(yield) >(yield)); + } + + walk_result users(hl::TypeDeclOp op, auto scope, auto &&yield) { + return type_users([&](mlir_type ty) { + if (auto rt = mlir::dyn_cast< RecordType >(ty)) + return rt.getName() == op.getName(); + return false; + }, scope, std::forward< decltype(yield) >(yield)); + } + + walk_result users(aggregate_interface op, auto scope, auto &&yield) { + return type_users([&](mlir_type ty) { + if (auto rt = mlir::dyn_cast< RecordType >(ty)) + return rt.getName() == op.getDefinedName(); + return false; + }, scope, std::forward< decltype(yield) >(yield)); + } + + walk_result users(hl::VarDeclOp var, auto scope, auto &&yield) { + VAST_CHECK(var.hasGlobalStorage(), "Only global variables are supported"); + return scope.walk([&](GlobalRefOp op) { + return op.getGlobal() == var.getName() ? yield(op) : walk_result::advance(); + }); + } + + walk_result users(hl::FuncOp op, auto scope, auto &&yield) { + for (auto user : util::symbol_users(op, scope)) { + if (auto result = yield(user); result == walk_result::interrupt()) { + return result; } - ++idx; } - return {}; + + return walk_result::advance(); } + } // namespace vast::hl diff --git a/include/vast/Dialect/HighLevel/Passes.hpp b/include/vast/Dialect/HighLevel/Passes.hpp index 626b88431f..ee756c315a 100644 --- a/include/vast/Dialect/HighLevel/Passes.hpp +++ b/include/vast/Dialect/HighLevel/Passes.hpp @@ -22,6 +22,8 @@ namespace vast::hl { std::unique_ptr< mlir::Pass > createDCEPass(); + std::unique_ptr< mlir::Pass > createUDEPass(); + std::unique_ptr< mlir::Pass > createLowerTypeDefsPass(); std::unique_ptr< mlir::Pass > createLowerElaboratedTypesPass(); diff --git a/include/vast/Dialect/HighLevel/Passes.td b/include/vast/Dialect/HighLevel/Passes.td index 2777eb4643..1e11850065 100644 --- a/include/vast/Dialect/HighLevel/Passes.td +++ b/include/vast/Dialect/HighLevel/Passes.td @@ -38,6 +38,20 @@ def DCE : Pass<"vast-hl-dce", "mlir::ModuleOp"> { let constructor = "vast::hl::createDCEPass()"; } +def UDE : Pass<"vast-hl-ude", "mlir::ModuleOp"> { + let summary = "Eliminate unused definitions"; + let description = [{ + Removes unused definitions, such as typedefs, structs and functions. + }]; + + let dependentDialects = [ + "vast::hl::HighLevelDialect", + "vast::core::CoreDialect" + ]; + + let constructor = "vast::hl::createUDEPass()"; +} + def HLLowerTypes : Pass<"vast-hl-lower-types", "mlir::ModuleOp"> { let summary = "Lower high-level types to standard types"; let description = [{ diff --git a/include/vast/Frontend/Pipelines.hpp b/include/vast/Frontend/Pipelines.hpp index 8d4d19923d..85e65c5881 100644 --- a/include/vast/Frontend/Pipelines.hpp +++ b/include/vast/Frontend/Pipelines.hpp @@ -16,6 +16,24 @@ namespace vast::cc { enum class pipeline_source { ast }; + struct vast_pipeline : pipeline_t + { + using base = pipeline_t; + + vast_pipeline(mcontext_t &mctx, const vast_args &vargs) + : base(&mctx), vargs(vargs) + {} + + virtual ~vast_pipeline() = default; + + void schedule(pipeline_step_ptr step) override; + + bool is_disabled(const pipeline_step_ptr &step) const; + + const vast_args &vargs; + }; + + // // Create pipeline schedule from source `src` to target `trg` // @@ -27,11 +45,10 @@ namespace vast::cc { // If the target is LLVM IR or other downstream target, the pipeline will // proceed into LLVM dialect. // - std::unique_ptr< pipeline_t > setup_pipeline( + std::unique_ptr< vast_pipeline > setup_pipeline( pipeline_source src, target_dialect trg, mcontext_t &mctx, const vast_args &vargs ); } // namespace vast::cc - diff --git a/include/vast/Interfaces/AggregateTypeDefinitionInterface.hpp b/include/vast/Interfaces/AggregateTypeDefinitionInterface.hpp index 77ae56b185..f5e28c796e 100644 --- a/include/vast/Interfaces/AggregateTypeDefinitionInterface.hpp +++ b/include/vast/Interfaces/AggregateTypeDefinitionInterface.hpp @@ -11,7 +11,20 @@ VAST_RELAX_WARNINGS #include VAST_RELAX_WARNINGS +#include "vast/Util/Common.hpp" + #include +namespace vast { + + struct field_info_t + { + std::string name; + mlir_type type; + }; + +} // namespace vast + + /// Include the generated interface declarations. #include "vast/Interfaces/AggregateTypeDefinitionInterface.h.inc" diff --git a/include/vast/Interfaces/AggregateTypeDefinitionInterface.td b/include/vast/Interfaces/AggregateTypeDefinitionInterface.td index 7860305ade..11c13b1f64 100644 --- a/include/vast/Interfaces/AggregateTypeDefinitionInterface.td +++ b/include/vast/Interfaces/AggregateTypeDefinitionInterface.td @@ -18,15 +18,17 @@ def AggregateTypeDefinition "gap::generator< mlir::Type >", "getFieldTypes", (ins), [{}] >, InterfaceMethod< "Return all elements in order of their declaration.", - "gap::generator< std::tuple< std::string, mlir::Type > >", - "getFieldsInfo", (ins), [{}] >, + "gap::generator< vast::field_info_t >", "getFieldsInfo", (ins), [{}] >, InterfaceMethod< "Return all nested definitions", "gap::generator< vast::AggregateTypeDefinitionInterface >", "getNestedDeclarations", (ins), [{}] >, InterfaceMethod< "Get name of the defined type", - "std::string", "getDefinedName", (ins), [{}] > + "llvm::StringRef", "getDefinedName", (ins), [{}] >, + + InterfaceMethod< "Get defined type", + "mlir::Type", "getDefinedType", (ins), [{}] > ]; } diff --git a/include/vast/Util/Common.hpp b/include/vast/Util/Common.hpp index f6496dfe6f..514bb2d8f3 100644 --- a/include/vast/Util/Common.hpp +++ b/include/vast/Util/Common.hpp @@ -85,4 +85,6 @@ namespace vast { using block_t = mlir::Block; using block_ptr = block_t*; + + using walk_result = mlir::WalkResult; } // namespace vast diff --git a/include/vast/Util/DataLayout.hpp b/include/vast/Util/DataLayout.hpp index 9c6289de86..8f7ceb8349 100644 --- a/include/vast/Util/DataLayout.hpp +++ b/include/vast/Util/DataLayout.hpp @@ -95,6 +95,19 @@ namespace vast::dl { bool operator==(const DLEntry &o) const = default; }; + void filter_data_layout(vast_module mod, auto &&filter) { + auto dl = mod.getDataLayoutSpec(); + + auto filtered_entries = llvm::to_vector( + llvm::make_filter_range(dl.getEntries(), std::forward< decltype(filter) >(filter)) + ); + + mod->setAttr( + mlir::DLTIDialect::kDataLayoutAttrName, + mlir::DataLayoutSpecAttr::get(mod.getContext(), filtered_entries) + ); + } + // For each type remember its data layout information. struct DataLayoutBlueprint { diff --git a/include/vast/Util/Pipeline.hpp b/include/vast/Util/Pipeline.hpp index 22d88cf4c6..f310a011d5 100644 --- a/include/vast/Util/Pipeline.hpp +++ b/include/vast/Util/Pipeline.hpp @@ -18,6 +18,14 @@ VAST_UNRELAX_WARNINGS namespace vast { +#if !defined(NDEBUG) + constexpr bool debug_pipelines = false; + #define VAST_PIPELINE_DEBUG(...) VAST_REPORT_WITH_PREFIX_IF(debug_pipelines, "[pipeline] ", __VA_ARGS__) +#else + #define VAST_PIPELINE_DEBUG(...) +#endif + + // // pipeline_step is a single step in the pipeline that is a pass or a list // of pipelines @@ -28,7 +36,6 @@ namespace vast { using pipeline_step_ptr = std::unique_ptr< pipeline_step >; - // // pipeline is a pass manager, which keeps track of duplicit passes and does // not schedule them twice @@ -40,6 +47,8 @@ namespace vast { using base::base; + virtual ~pipeline_t() = default; + void addPass(std::unique_ptr< mlir::Pass > pass); template< typename parent_t > @@ -50,10 +59,11 @@ namespace vast { } seen.insert(id); + VAST_PIPELINE_DEBUG("scheduling nested pass: {0}", pass->getName()); base::addNestedPass< parent_t >(std::move(pass)); } - friend pipeline_t &operator<<(pipeline_t &ppl, pipeline_step_ptr pass); + virtual void schedule(pipeline_step_ptr step) = 0; llvm::DenseSet< pass_id_t > seen; }; @@ -85,27 +95,27 @@ namespace vast { struct pipeline_step { explicit pipeline_step( - std::vector< pipeline_step_builder > dependencies + std::vector< pipeline_step_builder > deps ) - : dependencies(std::move(dependencies)) + : deps(std::move(deps)) {} explicit pipeline_step() = default; - virtual ~pipeline_step() = default; - virtual void schedule_on(pipeline_t &ppl) const; + virtual void schedule_on(pipeline_t &ppl) const = 0; + virtual gap::generator< pipeline_step_ptr > substeps() const = 0; - virtual string_ref name() const = 0; + gap::generator< pipeline_step_ptr > dependencies() const; - void schedule_dependencies(pipeline_t &ppl) const; + virtual string_ref name() const = 0; template< typename ...deps_t > - void depends_on(deps_t &&... deps) { - (dependencies.emplace_back(std::forward< deps_t >(deps)), ...); + void depends_on(deps_t &&... dep) { + (deps.emplace_back(std::forward< deps_t >(dep)), ...); } - std::vector< pipeline_step_builder > dependencies; + std::vector< pipeline_step_builder > deps; }; using pass_builder_t = llvm::function_ref< std::unique_ptr< mlir::Pass >(void) >; @@ -118,6 +128,8 @@ namespace vast { void schedule_on(pipeline_t &ppl) const override; + gap::generator< pipeline_step_ptr > substeps() const override; + string_ref name() const override; protected: @@ -131,8 +143,9 @@ namespace vast { : pass_pipeline_step(builder) {} + virtual ~nested_pass_pipeline_step() = default; + void schedule_on(pipeline_t &ppl) const override { - schedule_dependencies(ppl); ppl.addNestedPass< parent_t >(pass_builder()); } }; @@ -145,8 +158,12 @@ namespace vast { : pipeline_name(name), steps{ std::forward< steps_t >(steps)... } {} + virtual ~compound_pipeline_step() = default; + void schedule_on(pipeline_t &ppl) const override; + gap::generator< pipeline_step_ptr > substeps() const override; + string_ref name() const override; protected: diff --git a/include/vast/Util/Symbols.hpp b/include/vast/Util/Symbols.hpp index 9855e84d54..69c40a2cf1 100644 --- a/include/vast/Util/Symbols.hpp +++ b/include/vast/Util/Symbols.hpp @@ -50,18 +50,30 @@ namespace vast::util static inline auto symbol_name(mlir_symbol_interface value) { return value.getName(); } - void yield_symbol_users(vast_symbol_interface op, auto scope, auto &&yield) { + gap::generator< operation > symbol_users(vast_symbol_interface op, auto scope) { for (auto user : op->getUsers()) { + co_yield user; + } + } + + void yield_symbol_users(vast_symbol_interface op, auto scope, auto &&yield) { + for (auto user : symbol_users(op, scope)) { yield(user); } }; - void yield_symbol_users(mlir_symbol_interface op, auto scope, auto &&yield) { + gap::generator< operation > symbol_users(mlir_symbol_interface op, auto scope) { if (auto users = op.getSymbolUses(scope)) { for (auto use : *users) { - yield(use.getUser()); + co_yield use.getUser(); } } + } + + void yield_symbol_users(mlir_symbol_interface op, auto scope, auto &&yield) { + for (auto user : symbol_users(op, scope)) { + yield(user); + } }; void yield_users(string_ref symbol, auto scope, auto &&yield) { diff --git a/include/vast/Util/TypeUtils.hpp b/include/vast/Util/TypeUtils.hpp index b4c96eeacc..f018bc4ffc 100644 --- a/include/vast/Util/TypeUtils.hpp +++ b/include/vast/Util/TypeUtils.hpp @@ -106,6 +106,14 @@ namespace vast return has_type_somewhere(op, accept); } + template< typename user_filter, typename yield_t > + walk_result type_users(user_filter &&is_user, auto scope, yield_t &&yield) { + return scope.walk([&](operation op) { + return has_type_somewhere(op, std::forward< user_filter >(is_user)) + ? yield(op) : walk_result::advance(); + }); + } + auto bw(const auto &dl, mlir_type type) { return dl.getTypeSizeInBits(type); } auto bw(const auto &dl, auto type_range) diff --git a/include/vast/Util/Warnings.hpp b/include/vast/Util/Warnings.hpp index 9de10913d5..def538f6e4 100644 --- a/include/vast/Util/Warnings.hpp +++ b/include/vast/Util/Warnings.hpp @@ -92,6 +92,22 @@ namespace vast { vast_debug() << "[VAST debug] " << llvm::formatv(__VA_ARGS__) << "\n"; \ } while(0) + #define VAST_REPORT_WITH_PREFIX(prefix, ...) do { \ + vast_debug() << "[VAST debug] " << prefix << llvm::formatv(__VA_ARGS__) << "\n"; \ + } while(0) + + #define VAST_REPORT_IF(cond, ...) do { \ + if constexpr (cond) { \ + VAST_REPORT(__VA_ARGS__); \ + } \ + } while(0) + + #define VAST_REPORT_WITH_PREFIX_IF(cond, prefix, ...) do { \ + if constexpr (cond) { \ + VAST_REPORT_WITH_PREFIX(prefix, __VA_ARGS__); \ + } \ + } while(0) + #define VAST_UNREACHABLE(...) do { \ VAST_ERROR(__VA_ARGS__); \ llvm_unreachable(nullptr); \ diff --git a/lib/vast/Conversion/FromHL/ToLLGEPs.cpp b/lib/vast/Conversion/FromHL/ToLLGEPs.cpp index f79e5b6b2c..fcace3f85e 100644 --- a/lib/vast/Conversion/FromHL/ToLLGEPs.cpp +++ b/lib/vast/Conversion/FromHL/ToLLGEPs.cpp @@ -51,7 +51,7 @@ namespace vast { op_t op, typename op_t::Adaptor ops, conversion_rewriter &rewriter, hl::StructDeclOp struct_decl ) const { - auto idx = hl::field_idx(op.getName(), struct_decl); + auto idx = hl::field_index(op.getName(), struct_decl); if (!idx) { return mlir::failure(); } diff --git a/lib/vast/Dialect/HighLevel/HighLevelOps.cpp b/lib/vast/Dialect/HighLevel/HighLevelOps.cpp index 81a1e56716..3375bacb7b 100644 --- a/lib/vast/Dialect/HighLevel/HighLevelOps.cpp +++ b/lib/vast/Dialect/HighLevel/HighLevelOps.cpp @@ -370,6 +370,14 @@ namespace vast::hl build_region(bld, st, expr); } + mlir_type TypeDeclOp::getDefinedType() { + return hl::RecordType::get(getContext(), getName()); + } + + mlir_type TypeDefOp::getDefinedType() { + return hl::TypedefType::get(getContext(), getName()); + } + FuncOp getCallee(CallOp call) { auto coi = mlir::cast(call.getOperation()); diff --git a/lib/vast/Dialect/HighLevel/Passes.cpp b/lib/vast/Dialect/HighLevel/Passes.cpp index dd33fe3470..47d309c487 100644 --- a/lib/vast/Dialect/HighLevel/Passes.cpp +++ b/lib/vast/Dialect/HighLevel/Passes.cpp @@ -47,8 +47,12 @@ namespace vast::hl::pipeline { return pass(hl::createDCEPass).depends_on(canonicalize); } + static pipeline_step_ptr ude() { + return pass(hl::createUDEPass).depends_on(canonicalize); + } + pipeline_step_ptr simplify() { - return compose("simplify", dce, desugar); + return compose("simplify", ude, dce, desugar); } // diff --git a/lib/vast/Dialect/HighLevel/Transforms/CMakeLists.txt b/lib/vast/Dialect/HighLevel/Transforms/CMakeLists.txt index 0b0a908909..66fc8da163 100644 --- a/lib/vast/Dialect/HighLevel/Transforms/CMakeLists.txt +++ b/lib/vast/Dialect/HighLevel/Transforms/CMakeLists.txt @@ -7,4 +7,5 @@ add_vast_conversion_library(HighLevelTransforms LowerElaboratedTypes.cpp LowerTypeDefs.cpp SpliceTrailingScopes.cpp + UDE.cpp ) diff --git a/lib/vast/Dialect/HighLevel/Transforms/UDE.cpp b/lib/vast/Dialect/HighLevel/Transforms/UDE.cpp new file mode 100644 index 0000000000..4692a36c1a --- /dev/null +++ b/lib/vast/Dialect/HighLevel/Transforms/UDE.cpp @@ -0,0 +1,181 @@ +// Copyright (c) 2021-present, Trail of Bits, Inc. + +#include "vast/Dialect/HighLevel/Passes.hpp" + +VAST_RELAX_WARNINGS +#include +#include + +#include +#include +#include +VAST_UNRELAX_WARNINGS + +#include +#include + +#include +#include + +#include +#include + +#include "PassesDetails.hpp" + +#include + +namespace vast::hl { + +#if !defined(NDEBUG) + constexpr bool debug_ude_pass = false; + #define VAST_UDE_DEBUG(...) VAST_REPORT_WITH_PREFIX_IF(debug_ude_pass, "[UDE] ", __VA_ARGS__) +#else + #define VAST_UDE_DEBUG(...) +#endif + + constexpr bool keep_only_if_used = false; + + template< typename yield_t > + walk_result users(hl::FieldDeclOp decl, auto scope, yield_t &&yield) { + return hl::users(decl.getParentAggregate(), scope, std::forward< yield_t >(yield)); + } + + struct UDE : UDEBase< UDE > + { + using base = UDEBase< UDE >; + + std::unordered_set< operation > unused_cached; + + bool keep(aggregate_interface op, auto scope) const { return keep_only_if_used; } + bool keep(hl::TypeDefOp op, auto scope) const { return keep_only_if_used; } + bool keep(hl::TypeDeclOp op, auto scope) const { return keep_only_if_used; } + + // Mark field to be kept if the parent aggregate is kept + bool keep(hl::FieldDeclOp op, auto scope) const { return keep(op.getParentAggregate(), scope); } + + bool keep(hl::FuncOp op, auto scope) const { + return !op.isDeclaration() && !op->hasAttr( hl::AlwaysInlineAttr::getMnemonic() ); + } + + bool keep(hl::VarDeclOp op, auto scope) const { + VAST_CHECK(!op.hasExternalStorage() || op.getInitializer().empty(), "extern variable with initializer"); + return !op.hasExternalStorage(); + } + + bool is_unused_impl(auto op, auto scope, auto &seen) { + if (keep(op, scope)) { + VAST_UDE_DEBUG("keep: {0}", *op); + return false; + } + + auto result = hl::users(op, scope, [&](operation user) -> walk_result { + VAST_UDE_DEBUG("user: {0} of {1}", *user, *op); + // Ignore top-level use + if (user == scope) { + return walk_result::advance(); + } + + if (is_unused(user, scope, seen)) { + VAST_UDE_DEBUG("unused user: {0}", *user); + return walk_result::advance(); + } + + // If the user is an always inlined function that is not used, + // we mark it as unused. + if (auto parent = user->template getParentOfType< hl::FuncOp >()) { + if (is_unused(parent, scope, seen)) { + VAST_UDE_DEBUG("user in always inlined function: {0}", *user); + return walk_result::advance(); + } + } + + // We interrupt the walk if the operation is used + return walk_result::interrupt(); + }); + + // Operation is used if the walk was interrupted so we need to keep it. + return !result.wasInterrupted(); + } + + bool is_unused(operation op, auto scope, auto &seen) { + if (unused_cached.contains(op)) { + VAST_UDE_DEBUG("cached: {0}", *op); + return true; + } + + if (const auto [_, inserted] = seen.insert(op); !inserted) { + VAST_UDE_DEBUG("recursive: {0}", *op); + // Already processed, the operation has recursive dependency. + // We can safely return true here, as some other user + // needs to determine if the operation is to be kept. + return true; + } + + VAST_UDE_DEBUG("processing: {0}", *op); + bool result = llvm::TypeSwitch< operation, bool >(op) + .Case([&](aggregate_interface op) { return is_unused_impl(op, scope, seen); }) + .Case([&](hl::FieldDeclOp op) { return is_unused_impl(op, scope, seen); }) + .Case([&](hl::TypeDefOp op) { return is_unused_impl(op, scope, seen); }) + .Case([&](hl::TypeDeclOp op) { return is_unused_impl(op, scope, seen); }) + .Case([&](hl::FuncOp op) { return is_unused_impl(op, scope, seen); }) + .Case([&](hl::VarDeclOp op) { return is_unused_impl(op, scope, seen); }) + .Default([&](operation) { return false; }); + + if (result) { + unused_cached.insert(op); + } + + return result; + } + + std::vector< operation > gather_unused(auto scope) { + std::vector< operation > unused_operations; + for (auto &op : scope.getOps()) { + std::unordered_set< operation > seen; + if (is_unused(&op, scope, seen)) { + unused_operations.push_back(&op); + } + } + + return unused_operations; + } + + void runOnOperation() override { + auto mod = getOperation(); + auto unused = gather_unused(mod); + + llvm::DenseSet< mlir_type > unused_types; + for (auto &op : unused) { + if (auto td = mlir::dyn_cast< hl::TypeDefOp >(op)) { + unused_types.insert(td.getDefinedType()); + } + + if (auto agg = mlir::dyn_cast< aggregate_interface >(op)) { + unused_types.insert(agg.getDefinedType()); + } + + if (auto td = mlir::dyn_cast< hl::TypeDeclOp >(op)) { + unused_types.insert(td.getDefinedType()); + } + } + + auto contains_unused_subtype = [&] (mlir_type type) { + return contains_subtype(type, [&] (mlir_type sub) { + return unused_types.contains(sub); + }); + }; + + dl::filter_data_layout(mod, [&] (const auto &entry) { + auto type = entry.getKey().template get< mlir_type >(); + return !contains_unused_subtype(type); + }); + + for (auto op : unused) { + op->erase(); + } + } + }; + + std::unique_ptr< mlir::Pass > createUDEPass() { return std::make_unique< UDE >(); } + +} // namespace vast::hl diff --git a/lib/vast/Frontend/Pipelines.cpp b/lib/vast/Frontend/Pipelines.cpp index b1949a7789..4260aa8666 100644 --- a/lib/vast/Frontend/Pipelines.cpp +++ b/lib/vast/Frontend/Pipelines.cpp @@ -19,7 +19,6 @@ namespace vast::cc { // Simplifies high level MLIR pipeline_step_ptr reduce_high_level() { return compose("reduce-hl", - hl::pipeline::desugar, hl::pipeline::simplify ); } @@ -40,9 +39,9 @@ namespace vast::cc { return conv::pipeline::to_llvm(); } - pipeline_step_ptr codegen() { + gap::generator< pipeline_step_ptr > codegen() { // TODO: pass further options to augment high level MLIR - return high_level(); + co_yield high_level(); } // Defines a sequence of dialects and for each conversion dialect a @@ -59,11 +58,6 @@ namespace vast::cc { { target_dialect::llvm, { llvm } } }; - bool is_disabled(const pipeline_step_ptr &step, const vast_args &vargs) { - auto disable_step_option = opt::disable(step->name()).str(); - return vargs.has_option(disable_step_option); - } - gap::generator< pipeline_step_ptr > conversion( pipeline_source src, target_dialect trg, @@ -81,13 +75,7 @@ namespace vast::cc { for (const auto &[dialect, step_passes] : path) { for (auto &step : step_passes) { - auto pipeline_step = step(); - if (is_disabled(pipeline_step, vargs)) { - VAST_REPORT("Skipping disabled pipeline step: {0}", pipeline_step->name()); - continue; - } - VAST_REPORT("Adding pipeline step: {0}", pipeline_step->name()); - co_yield pipeline_step; + co_yield step(); } if (trg == dialect) { @@ -98,13 +86,31 @@ namespace vast::cc { } // namespace pipeline - std::unique_ptr< pipeline_t > setup_pipeline( + bool vast_pipeline::is_disabled(const pipeline_step_ptr &step) const { + auto disable_step_option = opt::disable(step->name()).str(); + return vargs.has_option(disable_step_option); + } + + void vast_pipeline::schedule(pipeline_step_ptr step) { + if (is_disabled(step)) { + VAST_PIPELINE_DEBUG("step is disabled: {0}", step->name()); + return; + } + + for (auto &&dep : step->dependencies()) { + schedule(std::move(dep)); + } + + step->schedule_on(*this); + } + + std::unique_ptr< vast_pipeline > setup_pipeline( pipeline_source src, target_dialect trg, mcontext_t &mctx, const vast_args &vargs ) { - auto passes = std::make_unique< pipeline_t >(&mctx); + auto passes = std::make_unique< vast_pipeline >(mctx, vargs); passes->enableIRPrinting( [](auto *, auto *) { return false; }, // before @@ -117,7 +123,9 @@ namespace vast::cc { // generate high level MLIR in case of AST input if (pipeline_source::ast == src) { - *passes << pipeline::codegen(); + for (auto &&step : pipeline::codegen()) { + passes->schedule(std::move(step)); + } } // Apply desired conversion to target dialect, if target is llvm or @@ -125,7 +133,7 @@ namespace vast::cc { // can specify how we want to convert to llvm dialect and allows to turn // off optional pipelines. for (auto &&step : pipeline::conversion(src, trg, vargs)) { - *passes << std::move(step); + passes->schedule(std::move(step)); } if (vargs.has_option(opt::print_pipeline)) { diff --git a/lib/vast/Util/Pipeline.cpp b/lib/vast/Util/Pipeline.cpp index 59da9f2de9..573b864170 100644 --- a/lib/vast/Util/Pipeline.cpp +++ b/lib/vast/Util/Pipeline.cpp @@ -12,24 +12,25 @@ namespace vast { } seen.insert(id); + VAST_PIPELINE_DEBUG("scheduling pass: {0}", pass->getName()); base::addPass(std::move(pass)); } - pipeline_t &operator<<(pipeline_t &ppl, pipeline_step_ptr pass) { - pass->schedule_on(ppl); - return ppl; + gap::generator< pipeline_step_ptr > pipeline_step::dependencies() const { + for (const auto &dep : deps) { + co_yield dep(); + } } - void pipeline_step::schedule_on(pipeline_t &) const {} + gap::generator< pipeline_step_ptr > pass_pipeline_step::substeps() const { co_return; } - void pipeline_step::schedule_dependencies(pipeline_t &ppl) const { - for (const auto &dep : dependencies) { - dep()->schedule_on(ppl); + gap::generator< pipeline_step_ptr > compound_pipeline_step::substeps() const { + for (const auto &step : steps) { + co_yield step(); } } void pass_pipeline_step::schedule_on(pipeline_t &ppl) const { - schedule_dependencies(ppl); ppl.addPass(pass_builder()); } @@ -38,9 +39,9 @@ namespace vast { } void compound_pipeline_step::schedule_on(pipeline_t &ppl) const { - schedule_dependencies(ppl); + VAST_PIPELINE_DEBUG("scheduling compound step: {0}", pipeline_name); for (const auto &step : steps) { - step()->schedule_on(ppl); + ppl.schedule(step()); } } diff --git a/test/vast/Transform/HL/UDE/func-a.c b/test/vast/Transform/HL/UDE/func-a.c new file mode 100644 index 0000000000..b98dfed78f --- /dev/null +++ b/test/vast/Transform/HL/UDE/func-a.c @@ -0,0 +1,7 @@ +// RUN: %vast-front -vast-emit-mlir=hl %s -o - | %vast-opt --vast-hl-ude | %file-check %s + +// CHECK-NOT: hl.func @unused_declaration +void unused_declaration(void); + +// CHECK: hl.func @unused_definition +void unused_definition(void) {} \ No newline at end of file diff --git a/test/vast/Transform/HL/UDE/func-b.c b/test/vast/Transform/HL/UDE/func-b.c new file mode 100644 index 0000000000..5d5624117c --- /dev/null +++ b/test/vast/Transform/HL/UDE/func-b.c @@ -0,0 +1,6 @@ +// RUN: %vast-front -vast-emit-mlir=hl %s -o - | %vast-opt --vast-hl-ude | %file-check %s + +// CHECK: hl.func @used_declaration +void used_declaration(void); + +void use(void) { used_declaration(); } \ No newline at end of file diff --git a/test/vast/Transform/HL/UDE/func-c.c b/test/vast/Transform/HL/UDE/func-c.c new file mode 100644 index 0000000000..d4ccbc8f15 --- /dev/null +++ b/test/vast/Transform/HL/UDE/func-c.c @@ -0,0 +1,8 @@ +// RUN: %vast-front -vast-emit-mlir=hl %s -o - | %vast-opt --vast-hl-ude | %file-check %s + +// CHECK: hl.func @used_declaration +void used_declaration(void); + +void use(void) { + void* addr = used_declaration; +} \ No newline at end of file diff --git a/test/vast/Transform/HL/UDE/func-d.c b/test/vast/Transform/HL/UDE/func-d.c new file mode 100644 index 0000000000..4b8cabc0d8 --- /dev/null +++ b/test/vast/Transform/HL/UDE/func-d.c @@ -0,0 +1,8 @@ +// RUN: %vast-front -vast-emit-mlir=hl %s -o - | %vast-opt --vast-hl-ude | %file-check %s + +// CHECK: hl.func @used_declaration +void used_declaration(void); + +void use(void) { + void* addr = &used_declaration; +} \ No newline at end of file diff --git a/test/vast/Transform/HL/UDE/func-e.c b/test/vast/Transform/HL/UDE/func-e.c new file mode 100644 index 0000000000..7db9adb0b3 --- /dev/null +++ b/test/vast/Transform/HL/UDE/func-e.c @@ -0,0 +1,19 @@ +// RUN: %vast-front -vast-emit-mlir=hl %s -o - | %vast-opt --vast-hl-ude | %file-check %s + +// CHECK-NOT: hl.struct "unused" +struct unused {}; + +// CHECK-NOT: hl.typedef "unused_t" +typedef struct unused unused_t; + +// CHECK-NOT: hl.struct "unused_rty" +struct unused_rty {}; + +// CHECK-NOT: hl.typedef "unused_rty_t" +typedef struct unused_rty unused_rty_t; + +// CHECK-NOT: hl.func @unused_declaration_with_typedefs +unused_rty_t unused_declaration_with_typedefs(unused_t); + +// CHECK-NOT: hl.func @unused_declaration_with_structs +struct unused_rty unused_declaration_with_structs(struct unused); diff --git a/test/vast/Transform/HL/UDE/func-f.c b/test/vast/Transform/HL/UDE/func-f.c new file mode 100644 index 0000000000..7db9adb0b3 --- /dev/null +++ b/test/vast/Transform/HL/UDE/func-f.c @@ -0,0 +1,19 @@ +// RUN: %vast-front -vast-emit-mlir=hl %s -o - | %vast-opt --vast-hl-ude | %file-check %s + +// CHECK-NOT: hl.struct "unused" +struct unused {}; + +// CHECK-NOT: hl.typedef "unused_t" +typedef struct unused unused_t; + +// CHECK-NOT: hl.struct "unused_rty" +struct unused_rty {}; + +// CHECK-NOT: hl.typedef "unused_rty_t" +typedef struct unused_rty unused_rty_t; + +// CHECK-NOT: hl.func @unused_declaration_with_typedefs +unused_rty_t unused_declaration_with_typedefs(unused_t); + +// CHECK-NOT: hl.func @unused_declaration_with_structs +struct unused_rty unused_declaration_with_structs(struct unused); diff --git a/test/vast/Transform/HL/UDE/func-g.c b/test/vast/Transform/HL/UDE/func-g.c new file mode 100644 index 0000000000..9b421bce7e --- /dev/null +++ b/test/vast/Transform/HL/UDE/func-g.c @@ -0,0 +1,18 @@ +// RUN: %vast-front -vast-emit-mlir=hl %s -o - | %vast-opt --vast-hl-ude | %file-check %s + +// CHECK-NOT: hl.struct "unused" +struct unused {}; + +// CHECK: hl.struct "used" +struct used {}; + +// CHECK-NOT: hl.func @inlined +__attribute__((always_inline)) void inlined() { + struct unused un; + struct used u; +} + +// CHECK: hl.func @not_inlined +void not_inlined() { + struct used u; +} \ No newline at end of file diff --git a/test/vast/Transform/HL/UDE/libc-a.c b/test/vast/Transform/HL/UDE/libc-a.c new file mode 100644 index 0000000000..2854a313f1 --- /dev/null +++ b/test/vast/Transform/HL/UDE/libc-a.c @@ -0,0 +1,7 @@ +// RUN: %vast-front -vast-emit-mlir=hl -vast-simplify %s -o - | %file-check %s +#include + +// CHECK-NOT: hl.typedef +// CHECK-NOT: hl.struct +// CHECK-NOT: hl.func +// CHECK-NOT: hl.var \ No newline at end of file diff --git a/test/vast/Transform/HL/UDE/struct-a.c b/test/vast/Transform/HL/UDE/struct-a.c new file mode 100644 index 0000000000..28f0751ec5 --- /dev/null +++ b/test/vast/Transform/HL/UDE/struct-a.c @@ -0,0 +1,11 @@ +// RUN: %vast-front -vast-emit-mlir=hl %s -o - | %vast-opt --vast-hl-ude | %file-check %s + +// CHECK: hl.struct "used" +struct used {}; + +// CHECK-NOT: hl.struct "unused" +struct unused {}; + +int main() { + struct used u; +} \ No newline at end of file diff --git a/test/vast/Transform/HL/UDE/struct-b.c b/test/vast/Transform/HL/UDE/struct-b.c new file mode 100644 index 0000000000..88ce51cc13 --- /dev/null +++ b/test/vast/Transform/HL/UDE/struct-b.c @@ -0,0 +1,13 @@ +// RUN: %vast-front -vast-emit-mlir=hl %s -o - | %vast-opt --vast-hl-ude | %file-check %s + +// CHECK: hl.struct "used" +struct used {}; + +// CHECK-NOT: hl.struct "unused" +struct unused { + struct used d; +}; + +int main() { + struct used u; +} \ No newline at end of file diff --git a/test/vast/Transform/HL/UDE/struct-c.c b/test/vast/Transform/HL/UDE/struct-c.c new file mode 100644 index 0000000000..d8bb407ac6 --- /dev/null +++ b/test/vast/Transform/HL/UDE/struct-c.c @@ -0,0 +1,19 @@ +// RUN: %vast-front -vast-emit-mlir=hl %s -o - | %vast-opt --vast-hl-ude | %file-check %s + +// CHECK: hl.struct "used" +struct used {}; + +// CHECK: hl.struct "depedent" +struct depedent +{ + struct used u; +}; + +// CHECK-NOT: hl.struct "unused" +struct unused { + struct used u; +}; + +int main() { + struct depedent d; +} \ No newline at end of file diff --git a/test/vast/Transform/HL/UDE/struct-d.c b/test/vast/Transform/HL/UDE/struct-d.c new file mode 100644 index 0000000000..5685a07220 --- /dev/null +++ b/test/vast/Transform/HL/UDE/struct-d.c @@ -0,0 +1,11 @@ +// RUN: %vast-front -vast-emit-mlir=hl %s -o - | %vast-opt --vast-hl-ude | %file-check %s + +// CHECK-NOT: hl.struct "in_unused" +struct in_unused {}; + +// CHECK-NOT: hl.struct "dependent_unused" +struct dependent_unused { + struct in_unused d; +}; + +int main() {} \ No newline at end of file diff --git a/test/vast/Transform/HL/UDE/struct-e.c b/test/vast/Transform/HL/UDE/struct-e.c new file mode 100644 index 0000000000..4e8b9f6b39 --- /dev/null +++ b/test/vast/Transform/HL/UDE/struct-e.c @@ -0,0 +1,9 @@ +// RUN: %vast-front -vast-emit-mlir=hl %s -o - | %vast-opt --vast-hl-ude | %file-check %s + +// CHECK: hl.struct "used" +struct used { int v; }; + +int main() { + struct used u; + u.v = 0; +} \ No newline at end of file diff --git a/test/vast/Transform/HL/UDE/struct-f.c b/test/vast/Transform/HL/UDE/struct-f.c new file mode 100644 index 0000000000..4247f7b6f1 --- /dev/null +++ b/test/vast/Transform/HL/UDE/struct-f.c @@ -0,0 +1,17 @@ +// RUN: %vast-front -vast-emit-mlir=hl %s -o - | %vast-opt --vast-hl-ude | %file-check %s + +// CHECK-NOT: hl.struct "unused_recursive" +struct unused_recursive { + struct unused_recursive *v; +}; + +// CHECK-NOT: hl.typedecl "unused_typedefed_recursive" +struct unused_typedefed_recursive; + +// CHECK-NOT: hl.struct "unused_recursive_t" +typedef struct unused_typedefed_recursive unused_recursive_t; + +// CHECK-NOT: hl.struct "unused_typedefed_recursive" +struct unused_typedefed_recursive { + unused_recursive_t *v; +}; \ No newline at end of file diff --git a/test/vast/Transform/HL/UDE/typedecl-a.c b/test/vast/Transform/HL/UDE/typedecl-a.c new file mode 100644 index 0000000000..2edeef63e3 --- /dev/null +++ b/test/vast/Transform/HL/UDE/typedecl-a.c @@ -0,0 +1,9 @@ +// RUN: %vast-front -vast-emit-mlir=hl %s -o - | %vast-opt --vast-hl-ude | %file-check %s + +// CHECK-NOT: hl.type "unused_typedecl" +struct unused_typedecl; + +// CHECK: hl.type "used_typedecl" +struct used_typedecl; + +void use(struct used_typedecl *) {} \ No newline at end of file diff --git a/test/vast/Transform/HL/UDE/typedef-a.c b/test/vast/Transform/HL/UDE/typedef-a.c new file mode 100644 index 0000000000..69df755cbb --- /dev/null +++ b/test/vast/Transform/HL/UDE/typedef-a.c @@ -0,0 +1,11 @@ +// RUN: %vast-front -vast-emit-mlir=hl %s -o - | %vast-opt --vast-hl-ude | %file-check %s + +// CHECK: hl.struct "used" +struct used {}; + +// CHECK-NOT: hl.typedef "unused_typedef" +typedef struct used unused_typedef; + +int main() { + struct used u; +} \ No newline at end of file diff --git a/test/vast/Transform/HL/UDE/typedef-b.c b/test/vast/Transform/HL/UDE/typedef-b.c new file mode 100644 index 0000000000..9398c485c8 --- /dev/null +++ b/test/vast/Transform/HL/UDE/typedef-b.c @@ -0,0 +1,14 @@ +// RUN: %vast-front -vast-emit-mlir=hl %s -o - | %vast-opt --vast-hl-ude | %file-check %s + +// CHECK: hl.struct "used" +struct used {}; + +// CHECK-NOT: hl.typedef "unused_typedef" +typedef struct used unused_typedef; + +// CHECK: hl.typedef "used_typedef" +typedef struct used used_typedef; + +int main() { + used_typedef u; +} \ No newline at end of file diff --git a/test/vast/Transform/HL/UDE/typedef-c.c b/test/vast/Transform/HL/UDE/typedef-c.c new file mode 100644 index 0000000000..7837d2e97b --- /dev/null +++ b/test/vast/Transform/HL/UDE/typedef-c.c @@ -0,0 +1,14 @@ +// RUN: %vast-front -vast-emit-mlir=hl %s -o - | %vast-opt --vast-hl-ude | %file-check %s + +// CHECK: hl.struct "used" +struct used {}; + +// CHECK-NOT: hl.typedef "unused_typedef" +typedef struct used unused_typedef; + +// CHECK-NOT: hl.typedef "used_typedef" +typedef unused_typedef unused_transitive_typedef; + +int main() { + struct used u; +} \ No newline at end of file diff --git a/test/vast/Transform/HL/UDE/typedef-d.c b/test/vast/Transform/HL/UDE/typedef-d.c new file mode 100644 index 0000000000..6e1edc29e2 --- /dev/null +++ b/test/vast/Transform/HL/UDE/typedef-d.c @@ -0,0 +1,10 @@ +// RUN: %vast-front -vast-emit-mlir=hl %s -o - | %vast-opt --vast-hl-ude | %file-check %s + +// CHECK-NOT: hl.struct "used" +struct unused_struct {}; + +// CHECK-NOT: hl.typedef "unused_typedef" +typedef struct unused_struct unused_typedef; + +// CHECK-NOT: hl.typedef "used_typedef" +typedef unused_typedef unused_transitive_typedef; diff --git a/test/vast/Transform/HL/UDE/union-a.c b/test/vast/Transform/HL/UDE/union-a.c new file mode 100644 index 0000000000..6f2aedfeec --- /dev/null +++ b/test/vast/Transform/HL/UDE/union-a.c @@ -0,0 +1,21 @@ +// RUN: %vast-front -vast-emit-mlir=hl %s -o - | %vast-opt --vast-hl-ude | %file-check %s + +// CHECK: hl.struct "used" +struct used +{ + int l; + int h; +}; + +// CHECK: hl.union "data" +union data +{ + unsigned long long b; + struct used s; +}; + +int main() +{ + union data d; + d.b = 0; +} diff --git a/test/vast/Transform/HL/UDE/union-b.c b/test/vast/Transform/HL/UDE/union-b.c new file mode 100644 index 0000000000..e87b2552f4 --- /dev/null +++ b/test/vast/Transform/HL/UDE/union-b.c @@ -0,0 +1,10 @@ +// RUN: %vast-front -vast-emit-mlir=hl %s -o - | %vast-opt --vast-hl-ude | %file-check %s + +// CHECK-NOT: hl.struct "used" +struct used {}; + +// CHECK-NOT: hl.union "unused_union" +union unused_union +{ + struct used s; +}; diff --git a/test/vast/Transform/HL/UDE/var-a.c b/test/vast/Transform/HL/UDE/var-a.c new file mode 100644 index 0000000000..126e86a6a5 --- /dev/null +++ b/test/vast/Transform/HL/UDE/var-a.c @@ -0,0 +1,15 @@ +// RUN: %vast-front -vast-emit-mlir=hl %s -o - | %vast-opt --vast-hl-ude | %file-check %s + +// CHECK: hl.var "a" +int a; + +// CHECK-NOT: hl.var "e" +extern int e; + +// CHECK: hl.var "s" +static int s; + +void local(void) { + // CHECK: hl.var "l" + int l; +} \ No newline at end of file diff --git a/test/vast/Transform/HL/UDE/var-b.c b/test/vast/Transform/HL/UDE/var-b.c new file mode 100644 index 0000000000..3ac6cbded5 --- /dev/null +++ b/test/vast/Transform/HL/UDE/var-b.c @@ -0,0 +1,8 @@ +// RUN: %vast-front -vast-emit-mlir=hl %s -o - | %vast-opt --vast-hl-ude | %file-check %s + +// CHECK: hl.var "used" +extern int used; + +void use() { + used = 0; +} \ No newline at end of file diff --git a/test/vast/Transform/HL/UDE/var-c.c b/test/vast/Transform/HL/UDE/var-c.c new file mode 100644 index 0000000000..ad94389235 --- /dev/null +++ b/test/vast/Transform/HL/UDE/var-c.c @@ -0,0 +1,10 @@ +// RUN: %vast-front -vast-emit-mlir=hl %s -o - | %vast-opt --vast-hl-ude | %file-check %s + +// CHECK-NOT: hl.typedecl "unused_struct" +struct unused_struct; + +// CHECK-NOT: hl.typedef "unused_t" +typedef struct unused_struct unused_t; + +// CHECK-NOT: hl.var "unused" +extern unused_t unused;