diff --git a/src/ast/passes/codegen_llvm.cpp b/src/ast/passes/codegen_llvm.cpp index 887b2874fdc..f2e0443d8aa 100644 --- a/src/ast/passes/codegen_llvm.cpp +++ b/src/ast/passes/codegen_llvm.cpp @@ -1800,7 +1800,8 @@ void CodegenLLVM::unop_ptr(Unop &unop) switch (unop.op) { case Operator::MUL: { if (unop.type.IsIntegerTy() || unop.type.IsPtrTy()) { - auto *et = type.GetPointeeTy(); + auto *et = type.IsRefTy() ? type.GetDereferencedType() + : type.GetPointeeTy(); AllocaInst *dst = b_.CreateAllocaBPF(*et, "deref"); b_.CreateProbeRead(ctx_, dst, *et, expr_, unop.loc, type.GetAS()); expr_ = b_.CreateLoad(b_.GetType(*et), dst); @@ -1826,7 +1827,10 @@ void CodegenLLVM::visit(Unop &unop) SizedType &type = unop.expr->type; if (type.IsIntegerTy()) { unop_int(unop); - } else if (type.IsPtrTy() || type.IsCtxAccess()) // allow dereferencing args + } else if (type.IsPtrTy() || type.IsRefTy() || + type.IsCtxAccess()) // allow + // dereferencing + // args { unop_ptr(unop); } else { @@ -3791,7 +3795,7 @@ void CodegenLLVM::readDatastructElemFromStack(Value *src_data, if (src->getType() != dst_type->getPointerTo()) src = b_.CreatePointerCast(src, dst_type->getPointerTo()); - if (elem_type.IsIntegerTy() || elem_type.IsPtrTy()) { + if (elem_type.IsIntegerTy() || elem_type.IsPtrTy() || elem_type.IsRefTy()) { // Load the correct type from src expr_ = b_.CreateDatastructElemLoad( elem_type, src, true, elem_type.GetAS()); diff --git a/src/ast/passes/field_analyser.cpp b/src/ast/passes/field_analyser.cpp index e1d182ebccb..f799564b4a9 100644 --- a/src/ast/passes/field_analyser.cpp +++ b/src/ast/passes/field_analyser.cpp @@ -159,8 +159,10 @@ void FieldAnalyser::visit(AssignVarStatement &assignment) void FieldAnalyser::visit(Unop &unop) { Visit(*unop.expr); - if (unop.op == Operator::MUL && sized_type_.IsPtrTy()) { - sized_type_ = *sized_type_.GetPointeeTy(); + if (unop.op == Operator::MUL && + (sized_type_.IsPtrTy() || sized_type_.IsRefTy())) { + sized_type_ = sized_type_.IsPtrTy() ? *sized_type_.GetPointeeTy() + : *sized_type_.GetDereferencedType(); resolve_fields(sized_type_); } } diff --git a/src/ast/passes/semantic_analyser.cpp b/src/ast/passes/semantic_analyser.cpp index 906acdfe8bb..62b53bc0abf 100644 --- a/src/ast/passes/semantic_analyser.cpp +++ b/src/ast/passes/semantic_analyser.cpp @@ -531,6 +531,7 @@ void SemanticAnalyser::visit(Call &call) } expr.accept(*this); + dereference_if_needed((*call.vargs)[i]); } } @@ -1324,6 +1325,7 @@ void SemanticAnalyser::visit(Sizeof &szof) szof.type = CreateUInt64(); if (szof.expr) { szof.expr->accept(*this); + dereference_if_needed(szof.expr); szof.argtype = szof.expr->type; } resolve_struct_type(szof.argtype, szof.loc); @@ -1334,6 +1336,7 @@ void SemanticAnalyser::visit(Offsetof &ofof) ofof.type = CreateUInt64(); if (ofof.expr) { ofof.expr->accept(*this); + dereference_if_needed(ofof.expr); ofof.record = ofof.expr->type; } resolve_struct_type(ofof.record, ofof.loc); @@ -1440,6 +1443,7 @@ void SemanticAnalyser::visit(Map &map) for (unsigned int i = 0; i < map.vargs->size(); i++) { Expression *expr = map.vargs->at(i); expr->accept(*this); + dereference_if_needed(map.vargs->at(i)); // Insert a cast to 64 bits if needed by injecting // a cast into the ast. @@ -1508,7 +1512,9 @@ void SemanticAnalyser::visit(Variable &var) void SemanticAnalyser::visit(ArrayAccess &arr) { arr.expr->accept(*this); + dereference_if_needed(arr.expr); arr.indexpr->accept(*this); + dereference_if_needed(arr.indexpr); SizedType &type = arr.expr->type; SizedType &indextype = arr.indexpr->type; @@ -1749,7 +1755,10 @@ void SemanticAnalyser::binop_ptr(Binop &binop) void SemanticAnalyser::visit(Binop &binop) { binop.left->accept(*this); + dereference_if_needed(binop.left); + binop.right->accept(*this); + dereference_if_needed(binop.right); auto &lht = binop.left->type; auto &rht = binop.right->type; @@ -1852,6 +1861,10 @@ void SemanticAnalyser::visit(Unop &unop) } unop.expr->accept(*this); + if (!(unop.op == Operator::MUL && unop.expr->type.IsRefTy())) { + // Prevent dereferencing a deref + dereference_if_needed(unop.expr); + } auto valid_ptr_op = false; switch (unop.op) { @@ -1865,10 +1878,11 @@ void SemanticAnalyser::visit(Unop &unop) SizedType &type = unop.expr->type; if (is_final_pass()) { - // Unops are only allowed on ints (e.g. ~$x), dereference only on pointers - // and context (we allow args->field for backwards compatibility) + // Unops are only allowed on ints (e.g. ~$x), dereference only on pointers, + // references and context (we allow args->field for backwards compatibility) if (!type.IsIntegerTy() && - !((type.IsPtrTy() || type.IsCtxAccess()) && valid_ptr_op)) { + !((type.IsPtrTy() || type.IsRefTy() || type.IsCtxAccess()) && + valid_ptr_op)) { LOG(ERROR, unop.loc, err_) << "The " << opstr(unop) << " operator can not be used on expressions of type '" << type @@ -1920,8 +1934,11 @@ void SemanticAnalyser::visit(Unop &unop) void SemanticAnalyser::visit(Ternary &ternary) { ternary.cond->accept(*this); + dereference_if_needed(ternary.cond); ternary.left->accept(*this); + dereference_if_needed(ternary.left); ternary.right->accept(*this); + dereference_if_needed(ternary.right); const Type &cond = ternary.cond->type.GetTy(); const Type &lhs = ternary.left->type.GetTy(); const Type &rhs = ternary.right->type.GetTy(); @@ -1949,6 +1966,7 @@ void SemanticAnalyser::visit(Ternary &ternary) void SemanticAnalyser::visit(If &if_block) { if_block.cond->accept(*this); + dereference_if_needed(if_block.cond); if (is_final_pass()) { const Type &cond = if_block.cond->type.GetTy(); @@ -1965,6 +1983,7 @@ void SemanticAnalyser::visit(If &if_block) void SemanticAnalyser::visit(Unroll &unroll) { unroll.expr->accept(*this); + dereference_if_needed(unroll.expr); auto unroll_value = bpftrace_.get_int_literal(unroll.expr); if (!unroll_value.has_value()) { @@ -1987,8 +2006,10 @@ void SemanticAnalyser::visit(Jump &jump) { switch (jump.ident) { case JumpType::RETURN: - if (jump.return_value) + if (jump.return_value) { jump.return_value->accept(*this); + dereference_if_needed(jump.return_value); + } if (auto subprog = dynamic_cast(scope_)) { if ((subprog->return_type.IsVoidTy() != (jump.return_value == nullptr)) || @@ -2020,6 +2041,7 @@ void SemanticAnalyser::visit(While &while_block) } while_block.cond->accept(*this); + dereference_if_needed(while_block.cond); loop_depth_++; accept_statements(while_block.stmts); @@ -2148,6 +2170,7 @@ void SemanticAnalyser::visit(FieldAccess &acc) assert((acc.field.size() > 0) != (acc.index >= 0)); acc.expr->accept(*this); + dereference_if_needed(acc.expr); SizedType &type = acc.expr->type; @@ -2285,6 +2308,7 @@ void SemanticAnalyser::visit(FieldAccess &acc) void SemanticAnalyser::visit(Cast &cast) { cast.expr->accept(*this); + dereference_if_needed(cast.expr); // cast type is synthesised in parser, if it is a struct, it needs resolving resolve_struct_type(cast.type, cast.loc); @@ -2365,6 +2389,7 @@ void SemanticAnalyser::visit(Tuple &tuple) for (size_t i = 0; i < tuple.elems->size(); ++i) { Expression *elem = tuple.elems->at(i); elem->accept(*this); + dereference_if_needed(tuple.elems->at(i)); // If elem type is none that means that the tuple contains some // invalid cast (e.g., (0, (aaa)0)). In this case, skip the tuple @@ -2380,12 +2405,14 @@ void SemanticAnalyser::visit(Tuple &tuple) void SemanticAnalyser::visit(ExprStatement &expr) { expr.expr->accept(*this); + dereference_if_needed(expr.expr); } void SemanticAnalyser::visit(AssignMapStatement &assignment) { assignment.map->accept(*this); assignment.expr->accept(*this); + dereference_if_needed(assignment.expr); assign_map_type(*assignment.map, assignment.expr->type); @@ -2462,6 +2489,7 @@ void SemanticAnalyser::visit(AssignMapStatement &assignment) void SemanticAnalyser::visit(AssignVarStatement &assignment) { assignment.expr->accept(*this); + dereference_if_needed(assignment.expr); std::string var_ident = assignment.var->ident; auto search = variable_val_[scope_].find(var_ident); @@ -2542,11 +2570,13 @@ void SemanticAnalyser::visit(AssignVarStatement &assignment) void SemanticAnalyser::visit(AssignConfigVarStatement &assignment) { assignment.expr->accept(*this); + dereference_if_needed(assignment.expr); } void SemanticAnalyser::visit(Predicate &pred) { pred.expr->accept(*this); + dereference_if_needed(pred.expr); if (is_final_pass()) { SizedType &ty = pred.expr->type; if (!ty.IsIntTy() && !ty.IsPtrTy()) { @@ -3324,5 +3354,18 @@ Pass CreateSemanticPass() return Pass("Semantic", fn); }; +void SemanticAnalyser::dereference_if_needed(Expression *&expr) +{ + const auto &type = expr->type; + if (type.IsRefTy()) { + expr = new Unop(Operator::MUL, expr, expr->loc); + expr->type = *type.GetDereferencedType(); + expr->type.is_internal = type.is_internal; + expr->type.SetAS(type.GetAS()); + if (type.IsCtxAccess()) + expr->type.MarkCtxAccess(); + } +} + } // namespace ast } // namespace bpftrace diff --git a/src/ast/passes/semantic_analyser.h b/src/ast/passes/semantic_analyser.h index b4f24902182..40b8b162270 100644 --- a/src/ast/passes/semantic_analyser.h +++ b/src/ast/passes/semantic_analyser.h @@ -123,6 +123,7 @@ class SemanticAnalyser : public Visitor { void binop_ptr(Binop &op); void binop_int(Binop &op); void binop_array(Binop &op); + void dereference_if_needed(Expression *&expr); bool has_error() const; bool in_loop(void) diff --git a/src/dwarf_parser.cpp b/src/dwarf_parser.cpp index c0e2ba9449d..777e77e00f6 100644 --- a/src/dwarf_parser.cpp +++ b/src/dwarf_parser.cpp @@ -184,7 +184,7 @@ SizedType Dwarf::get_stype(lldb::SBType type, bool resolve_structs) case lldb::eTypeClassPointer: return CreatePointer(get_stype(type.GetPointeeType(), false)); case lldb::eTypeClassReference: - return CreatePointer(get_stype(type.GetDereferencedType(), false)); + return CreateReference(get_stype(type.GetDereferencedType(), false)); case lldb::eTypeClassClass: case lldb::eTypeClassStruct: case lldb::eTypeClassUnion: { diff --git a/src/types.cpp b/src/types.cpp index ecba0031f86..ff64b09cf1c 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -148,6 +148,7 @@ std::string typestr(Type t) case Type::voidtype: return "void"; break; case Type::integer: return "integer"; break; case Type::pointer: return "pointer"; break; + case Type::reference:return "reference";break; case Type::record: return "record"; break; case Type::hist: return "hist"; break; case Type::lhist: return "lhist"; break; @@ -354,6 +355,15 @@ SizedType CreatePointer(const SizedType &pointee_type, AddrSpace as) return ty; } +SizedType CreateReference(const SizedType &referred_type, AddrSpace as) +{ + // Pointer itself is always an uint64 + auto ty = SizedType(Type::reference, 8); + ty.element_type_ = std::make_shared(referred_type); + ty.SetAS(as); + return ty; +} + SizedType CreateRecord(const std::string &name, std::weak_ptr record) { auto ty = SizedType(Type::record, record.expired() ? 0 : record.lock()->size); @@ -584,6 +594,9 @@ size_t hash::operator()( case bpftrace::Type::pointer: bpftrace::hash_combine(hash, *type.GetPointeeTy()); break; + case bpftrace::Type::reference: + bpftrace::hash_combine(hash, *type.GetDereferencedType()); + break; case bpftrace::Type::record: bpftrace::hash_combine(hash, type.GetName()); break; diff --git a/src/types.h b/src/types.h index 66c85686ecc..a2cec7ba367 100644 --- a/src/types.h +++ b/src/types.h @@ -25,6 +25,7 @@ enum class Type : uint8_t { voidtype, integer, pointer, + reference, record, // struct/union, as struct is a protected keyword hist, lhist, @@ -284,6 +285,12 @@ class SizedType { return element_type_.get(); } + const SizedType *GetDereferencedType() const + { + assert(IsRefTy()); + return element_type_.get(); + } + bool IsBoolTy() const { return type_ == Type::integer && size_bits_ == 1; @@ -292,6 +299,10 @@ class SizedType { { return type_ == Type::pointer; }; + bool IsRefTy() const + { + return type_ == Type::reference; + }; bool IsIntTy() const { return type_ == Type::integer; @@ -422,6 +433,7 @@ class SizedType { const SizedType &element_type); friend SizedType CreatePointer(const SizedType &pointee_type, AddrSpace as); + friend SizedType CreateReference(const SizedType &pointee_type, AddrSpace as); friend SizedType CreateRecord(const std::string &name, std::weak_ptr record); friend SizedType CreateInteger(size_t bits, bool is_signed); @@ -448,6 +460,8 @@ SizedType CreateString(size_t size); SizedType CreateArray(size_t num_elements, const SizedType &element_type); SizedType CreatePointer(const SizedType &pointee_type, AddrSpace as = AddrSpace::none); +SizedType CreateReference(const SizedType &referred_type, + AddrSpace as = AddrSpace::none); SizedType CreateRecord(const std::string &name, std::weak_ptr record); SizedType CreateTuple(std::weak_ptr tuple); diff --git a/tests/data/data_source_cxx.cpp b/tests/data/data_source_cxx.cpp index 69311411931..267ee7da8b7 100644 --- a/tests/data/data_source_cxx.cpp +++ b/tests/data/data_source_cxx.cpp @@ -54,9 +54,10 @@ struct Bottom : public Left, public Right { struct Multi : public Parent, public Top { int abc; + int &rabc; Multi(int a, int b, int c, int d, int e) - : Parent{ a, b, c, d }, Top{ e }, abc{ e + 1 } + : Parent{ a, b, c, d }, Top{ e }, abc{ e + 1 }, rabc{ abc } { } }; diff --git a/tests/field_analyser.cpp b/tests/field_analyser.cpp index b4ab95f65e4..5e1ca122ee7 100644 --- a/tests/field_analyser.cpp +++ b/tests/field_analyser.cpp @@ -610,8 +610,8 @@ TEST_F(field_analyser_dwarf, parse_inheritance_multi) } ASSERT_TRUE(cls->HasFields()); - ASSERT_EQ(cls->fields.size(), 6); - ASSERT_EQ(cls->size, 24); + ASSERT_EQ(cls->fields.size(), 7); + ASSERT_EQ(cls->size, 32); CheckParentFields(cls); @@ -624,6 +624,11 @@ TEST_F(field_analyser_dwarf, parse_inheritance_multi) EXPECT_TRUE(cls->GetField("abc").type.IsIntTy()); EXPECT_EQ(cls->GetField("abc").type.GetSize(), 4); EXPECT_EQ(cls->GetField("abc").offset, 20); + + EXPECT_TRUE(cls->HasField("rabc")); + EXPECT_TRUE(cls->GetField("rabc").type.IsRefTy()); + EXPECT_EQ(cls->GetField("rabc").type.GetSize(), 8); + EXPECT_EQ(cls->GetField("rabc").offset, 24); } TEST_F(field_analyser_dwarf, parse_struct_anonymous_fields) diff --git a/tests/testprogs/uprobe_test_cxx.cpp b/tests/testprogs/uprobe_test_cxx.cpp index 45b64d61557..5f4c8346332 100644 --- a/tests/testprogs/uprobe_test_cxx.cpp +++ b/tests/testprogs/uprobe_test_cxx.cpp @@ -28,6 +28,15 @@ class Child : public Parent { } }; +struct Foo { + int a, b, c; +}; + +int uprobeFunction1(int &x, Foo &foo) +{ + return x + foo.c; +} + int uprobeFunction3(Child &c, Parent &p __attribute__((unused))) { return dynamic_cast(c).d; @@ -42,6 +51,10 @@ int main(int argc __attribute__((unused)), char **argv __attribute__((unused))) { // usleep(1000000); + int x = 42; + Foo foo{ 1, 2, 3 }; + uprobeFunction1(x, foo); + Parent p{ 1, 2, 3, 4 }; Child c{ 1, 2, 3, 4, 5, 6 };