Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unused Definition Elimination #522

Merged
merged 33 commits into from
Mar 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
4a43b0a
util: Add generator based symbol users.
xlauko Mar 5, 2024
bb664e8
cc: Allow to intercept each step of the pipeline.
xlauko Mar 6, 2024
e10e8b3
util: Add conditional debug prints.
xlauko Mar 6, 2024
60c055d
hl: Access parent aggregate from FieldOp.
xlauko Mar 6, 2024
f257e90
hl: Setup elimination of unused definitions (UDE).
xlauko Mar 6, 2024
da786d0
test: Add UDE struct/union passes.
xlauko Mar 6, 2024
f306ea6
hl: Refactor type definition users generators.
xlauko Mar 7, 2024
8b8ffbb
hl: Let UDE take care of typedefs.
xlauko Mar 7, 2024
6a91537
test: Add typedef UDE tests.
xlauko Mar 7, 2024
aa65cbd
hl: Refactor utilities.
xlauko Mar 7, 2024
3b2f68f
hl: Implement unused function elimination.
xlauko Mar 7, 2024
e35b6f1
test: Add function UDE tests.
xlauko Mar 7, 2024
5243362
hl: Deal with recursive unused definitions.
xlauko Mar 7, 2024
b416f7c
hl: Refactor type users implementation.
xlauko Mar 7, 2024
7c56bf8
hl: Make getDefinedName return reference.
xlauko Mar 7, 2024
d1ff7bd
hl: Implement unused typedecl elimination.
xlauko Mar 7, 2024
83945e8
test: Add recursive type UDE test.
xlauko Mar 7, 2024
7091046
test: Add typedecl UDE test.
xlauko Mar 7, 2024
11e2c83
hl: Implement always inline attribute.
xlauko Mar 7, 2024
e3b6fa2
hl: Remove unused always inlined functions.
xlauko Mar 7, 2024
8f6bc5c
hl: Mark users in inlined functions as unused.
xlauko Mar 7, 2024
27d37cf
hl: Unify comments style in UDE.
xlauko Mar 7, 2024
0bf3998
test: Add always inline function UDE test.
xlauko Mar 7, 2024
16232af
hl: Implement variable users for UDE.
xlauko Mar 7, 2024
39b0174
hl: Fix global variables users iteration.
xlauko Mar 7, 2024
95f9bff
hl: Implement UDE for variables.
xlauko Mar 7, 2024
87cf709
test: Add tests on unused global variables elimination.
xlauko Mar 7, 2024
60aab3c
hl: Refactor UDE pass.
xlauko Mar 8, 2024
a866bc6
hl: Allow to access typedef type from the definition.
xlauko Mar 8, 2024
fb6cfa0
hl: Add defined types accessors.
xlauko Mar 8, 2024
4bad9c5
hl: Clean up data layout after UDE.
xlauko Mar 8, 2024
f1503af
test: Add libc UDE test.
xlauko Mar 8, 2024
6aea65b
hl: Simplify UDE keep_only_if_used checks.
xlauko Mar 11, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions include/vast/CodeGen/CodeGenAttrVisitor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 >();
}
Expand Down
1 change: 1 addition & 0 deletions include/vast/Dialect/HighLevel/HighLevelAttributes.td
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ class NameAttr<string name, string attr_mnemonic>
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" >;
Expand Down
31 changes: 24 additions & 7 deletions include/vast/Dialect/HighLevel/HighLevelOps.td
Original file line number Diff line number Diff line change
Expand Up @@ -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 }];
}

Expand All @@ -85,6 +89,10 @@ def TypeDefOp
be referenced as NamedType later.
}];

let extraClassDeclaration = [{
mlir_type getDefinedType();
}];

let assemblyFormat = [{ $name attr-dict `:` $type }];
}

Expand Down Expand Up @@ -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,
Expand All @@ -218,31 +226,34 @@ class RecordLikeDeclOp< string mnemonic, string concrete_name, list< Trait > tra
let extraClassDefinition = [{
// AggregateTypeDefinitionInterface

gap::generator<mlir::Type> }] # concrete_name # [{ ::getFieldTypes() {
gap::generator< mlir_type > $cppClass::getFieldTypes() {
return hl::get_field_types(*this);
}

gap::generator<std::tuple<std::string, mlir::Type>>}] # 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 }];
}
Expand All @@ -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 }];
}

Expand Down
148 changes: 101 additions & 47 deletions include/vast/Dialect/HighLevel/HighLevelUtils.hpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) 2023-present, Trail of Bits, Inc.

Check notice on line 1 in include/vast/Dialect/HighLevel/HighLevelUtils.hpp

View workflow job for this annotation

GitHub Actions / cpp-linter (17, 22.04)

Run clang-format on include/vast/Dialect/HighLevel/HighLevelUtils.hpp

File include/vast/Dialect/HighLevel/HighLevelUtils.hpp does not conform to Custom style guidelines. (lines 71, 77, 87, 96, 106, 113, 115, 116, 117, 124, 136, 137, 138, 139, 140, 144, 145, 146, 147, 148, 152, 153, 154, 155, 156)

#pragma once

Expand All @@ -9,36 +9,41 @@
#include "vast/Interfaces/SymbolInterface.hpp"

#include "vast/Util/Common.hpp"
#include <vast/Util/Symbols.hpp>
#include "vast/Util/TypeUtils.hpp"

#include "gap/core/generator.hpp"
#include <gap/core/generator.hpp>

#include <ranges>

/* 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;
}
}
Expand All @@ -47,76 +52,125 @@
// 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 <typename T>
std::vector<T> to_vector(gap::generator<T> &&gen) {
std::vector<T> 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
2 changes: 2 additions & 0 deletions include/vast/Dialect/HighLevel/Passes.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
14 changes: 14 additions & 0 deletions include/vast/Dialect/HighLevel/Passes.td
Original file line number Diff line number Diff line change
Expand Up @@ -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 = [{
Expand Down
21 changes: 19 additions & 2 deletions include/vast/Frontend/Pipelines.hpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) 2023-present, Trail of Bits, Inc.

Check notice on line 1 in include/vast/Frontend/Pipelines.hpp

View workflow job for this annotation

GitHub Actions / cpp-linter (17, 22.04)

Run clang-format on include/vast/Frontend/Pipelines.hpp

File include/vast/Frontend/Pipelines.hpp does not conform to Custom style guidelines. (lines 23, 24, 34)

#pragma once

Expand All @@ -16,6 +16,24 @@

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`
//
Expand All @@ -27,11 +45,10 @@
// 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

Loading