Skip to content

[mlir][EmitC] Add member access ops #98460

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

Merged
merged 1 commit into from
Jul 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
42 changes: 42 additions & 0 deletions mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
Original file line number Diff line number Diff line change
Expand Up @@ -908,6 +908,48 @@ def EmitC_SubOp : EmitC_BinaryOp<"sub", [CExpression]> {
let hasVerifier = 1;
}

def EmitC_MemberOp : EmitC_Op<"member"> {
let summary = "Member operation";
let description = [{
With the `member` operation the member access operator `.` can be
applied.

Example:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is more just what the ops syntax looks like rather than an example to me. That is, if one didn't know what the op would generate then this doesn't help.


```mlir
%0 = "emitc.member" (%arg0) {member = "a"}
: (!emitc.opaque<"mystruct">) -> i32
```
}];

let arguments = (ins
Arg<StrAttr, "the member to access">:$member,
EmitC_OpaqueType:$operand
);
let results = (outs EmitCType);
}

def EmitC_MemberOfPtrOp : EmitC_Op<"member_of_ptr"> {
let summary = "Member of pointer operation";
let description = [{
With the `member_of_ptr` operation the member access operator `->`
can be applied.

Example:

```mlir
%0 = "emitc.member_of_ptr" (%arg0) {member = "a"}
: (!emitc.ptr<!emitc.opaque<"mystruct">>) -> i32
```
}];

let arguments = (ins
Arg<StrAttr, "the member to access">:$member,
AnyTypeOf<[EmitC_OpaqueType,EmitC_PointerType]>:$operand
);
let results = (outs EmitCType);
}

def EmitC_ConditionalOp : EmitC_Op<"conditional",
[AllTypesMatch<["true_value", "false_value", "result"]>, CExpression]> {
let summary = "Conditional (ternary) operation";
Expand Down
7 changes: 4 additions & 3 deletions mlir/lib/Dialect/EmitC/IR/EmitC.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -213,10 +213,11 @@ LogicalResult emitc::AssignOp::verify() {
Value variable = getVar();
Operation *variableDef = variable.getDefiningOp();
if (!variableDef ||
!llvm::isa<emitc::GetGlobalOp, emitc::SubscriptOp, emitc::VariableOp>(
variableDef))
!llvm::isa<emitc::GetGlobalOp, emitc::MemberOp, emitc::MemberOfPtrOp,
emitc::SubscriptOp, emitc::VariableOp>(variableDef))
return emitOpError() << "requires first operand (" << variable
<< ") to be a get_global, subscript or variable";
<< ") to be a get_global, member, member of pointer, "
"subscript or variable";

Value value = getValue();
if (variable.getType() != value.getType())
Expand Down
34 changes: 32 additions & 2 deletions mlir/lib/Target/Cpp/TranslateToCpp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,12 @@ struct CppEmitter {
// Returns the textual representation of a subscript operation.
std::string getSubscriptName(emitc::SubscriptOp op);

// Returns the textual representation of a member (of object) operation.
std::string createMemberAccess(emitc::MemberOp op);

// Returns the textual representation of a member of pointer operation.
std::string createMemberAccess(emitc::MemberOfPtrOp op);

/// Return the existing or a new label of a Block.
StringRef getOrCreateName(Block &block);

Expand Down Expand Up @@ -278,8 +284,8 @@ struct CppEmitter {

/// Determine whether expression \p op should be emitted in a deferred way.
static bool hasDeferredEmission(Operation *op) {
return isa_and_nonnull<emitc::GetGlobalOp, emitc::LiteralOp,
emitc::SubscriptOp>(op);
return isa_and_nonnull<emitc::GetGlobalOp, emitc::LiteralOp, emitc::MemberOp,
emitc::MemberOfPtrOp, emitc::SubscriptOp>(op);
}

/// Determine whether expression \p expressionOp should be emitted inline, i.e.
Expand Down Expand Up @@ -1125,6 +1131,22 @@ std::string CppEmitter::getSubscriptName(emitc::SubscriptOp op) {
return out;
}

std::string CppEmitter::createMemberAccess(emitc::MemberOp op) {
std::string out;
llvm::raw_string_ostream ss(out);
ss << getOrCreateName(op.getOperand());
ss << "." << op.getMember();
return out;
}

std::string CppEmitter::createMemberAccess(emitc::MemberOfPtrOp op) {
std::string out;
llvm::raw_string_ostream ss(out);
ss << getOrCreateName(op.getOperand());
ss << "->" << op.getMember();
return out;
}

void CppEmitter::cacheDeferredOpResult(Value value, StringRef str) {
if (!valueMapper.count(value))
valueMapper.insert(value, str.str());
Expand Down Expand Up @@ -1501,6 +1523,14 @@ LogicalResult CppEmitter::emitOperation(Operation &op, bool trailingSemicolon) {
cacheDeferredOpResult(op.getResult(), op.getValue());
return success();
})
.Case<emitc::MemberOp>([&](auto op) {
cacheDeferredOpResult(op.getResult(), createMemberAccess(op));
return success();
})
.Case<emitc::MemberOfPtrOp>([&](auto op) {
cacheDeferredOpResult(op.getResult(), createMemberAccess(op));
return success();
})
.Case<emitc::SubscriptOp>([&](auto op) {
cacheDeferredOpResult(op.getResult(), getSubscriptName(op));
return success();
Expand Down
18 changes: 17 additions & 1 deletion mlir/test/Dialect/EmitC/invalid_ops.mlir
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ func.func @test_misplaced_yield() {
// -----

func.func @test_assign_to_non_variable(%arg1: f32, %arg2: f32) {
// expected-error @+1 {{'emitc.assign' op requires first operand (<block argument> of type 'f32' at index: 1) to be a get_global, subscript or variable}}
// expected-error @+1 {{'emitc.assign' op requires first operand (<block argument> of type 'f32' at index: 1) to be a get_global, member, member of pointer, subscript or variable}}
emitc.assign %arg1 : f32 to %arg2 : f32
return
}
Expand Down Expand Up @@ -450,3 +450,19 @@ func.func @use_global() {
%0 = emitc.get_global @myglobal : f32
return
}

// -----

func.func @member(%arg0: i32) {
// expected-error @+1 {{'emitc.member' op operand #0 must be EmitC opaque type, but got 'i32'}}
%0 = "emitc.member" (%arg0) {member = "a"} : (i32) -> i32
return
}

// -----

func.func @member_of_ptr(%arg0: i32) {
// expected-error @+1 {{'emitc.member_of_ptr' op operand #0 must be EmitC opaque type or EmitC pointer type, but got 'i32}}
%0 = "emitc.member_of_ptr" (%arg0) {member = "a"} : (i32) -> i32
return
}
7 changes: 7 additions & 0 deletions mlir/test/Dialect/EmitC/ops.mlir
Original file line number Diff line number Diff line change
Expand Up @@ -254,3 +254,10 @@ func.func @assign_global(%arg0 : i32) {
emitc.assign %arg0 : i32 to %0 : i32
return
}

func.func @member_access(%arg0: !emitc.opaque<"mystruct">, %arg1: !emitc.opaque<"mystruct_ptr">, %arg2: !emitc.ptr<!emitc.opaque<"mystruct">>) {
%0 = "emitc.member" (%arg0) {member = "a"} : (!emitc.opaque<"mystruct">) -> i32
%1 = "emitc.member_of_ptr" (%arg1) {member = "a"} : (!emitc.opaque<"mystruct_ptr">) -> i32
%2 = "emitc.member_of_ptr" (%arg2) {member = "a"} : (!emitc.ptr<!emitc.opaque<"mystruct">>) -> i32
return
}
34 changes: 34 additions & 0 deletions mlir/test/Target/Cpp/member.mlir
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// RUN: mlir-translate -mlir-to-cpp %s | FileCheck %s -check-prefix=CPP-DEFAULT

func.func @member(%arg0: !emitc.opaque<"mystruct">, %arg1: i32) {
%0 = "emitc.member" (%arg0) {member = "a"} : (!emitc.opaque<"mystruct">) -> i32
emitc.assign %arg1 : i32 to %0 : i32

%1 = "emitc.member" (%arg0) {member = "b"} : (!emitc.opaque<"mystruct">) -> i32
%2 = "emitc.variable"() <{value = #emitc.opaque<"">}> : () -> i32
emitc.assign %1 : i32 to %2 : i32

return
}

// CPP-DEFAULT: void member(mystruct [[V0:[^ ]*]], int32_t [[V1:[^ ]*]]) {
// CPP-DEFAULT-NEXT: [[V0:[^ ]*]].a = [[V1:[^ ]*]];
// CPP-DEFAULT-NEXT: int32_t [[V2:[^ ]*]];
// CPP-DEFAULT-NEXT: [[V2:[^ ]*]] = [[V0:[^ ]*]].b;


func.func @member_of_pointer(%arg0: !emitc.ptr<!emitc.opaque<"mystruct">>, %arg1: i32) {
%0 = "emitc.member_of_ptr" (%arg0) {member = "a"} : (!emitc.ptr<!emitc.opaque<"mystruct">>) -> i32
emitc.assign %arg1 : i32 to %0 : i32

%1 = "emitc.member_of_ptr" (%arg0) {member = "b"} : (!emitc.ptr<!emitc.opaque<"mystruct">>) -> i32
%2 = "emitc.variable"() <{value = #emitc.opaque<"">}> : () -> i32
emitc.assign %1 : i32 to %2 : i32

return
}

// CPP-DEFAULT: void member_of_pointer(mystruct* [[V0:[^ ]*]], int32_t [[V1:[^ ]*]]) {
// CPP-DEFAULT-NEXT: [[V0:[^ ]*]]->a = [[V1:[^ ]*]];
// CPP-DEFAULT-NEXT: int32_t [[V2:[^ ]*]];
// CPP-DEFAULT-NEXT: [[V2:[^ ]*]] = [[V0:[^ ]*]]->b;
Loading