-
Notifications
You must be signed in to change notification settings - Fork 304
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
[Calyx] Binary Floating Point MulF Operator #7769
Conversation
Okay, I think we should move the floating point operators into their own separate definition because they are so much more complicated. Can we get the syntax: |
8bda83f
to
f54ffee
Compare
f54ffee
to
8a270d7
Compare
marking ready for review again! |
|
||
module attributes {calyx.entrypoint = "main"} { | ||
// CHECK: import "primitives/float/mulFN.futil"; | ||
calyx.component @main(%in0: i32, %clk: i1 {clk}, %reset: i1 {reset}, %go: i1 {go}) -> (%out0: i32, %done: i1 {done}) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add a CHECK-NEXT: for this line.
wait it is added here right? I believe you are looking for std_mulFN_0 = std_mulFN(8, 24, 32)
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, can this not use CHECK-NEXT
since we know exactly which line produces the primitive?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you mean we don't need to check std_mulFN_0 = std_mulFN(8, 24, 32)
at all?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, can we ensure that the code is generated precisely as a consequence of that one line?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds good, I'm updating to CHECK-DAG
to ensure your request of checking the code is generated by the correct line sequentially; as well as avoiding checking some boilerplate code that will be otherwise generated by CHECK-NEXT
. Please let me know if it makes sense.
5a4c97c
to
6a6d127
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A few smaller comments.
@@ -424,6 +424,10 @@ class ComponentLoweringStateInterface { | |||
Block *body = component.getBodyBlock(); | |||
builder.setInsertionPoint(body, body->begin()); | |||
auto name = TLibraryOp::getOperationName().split(".").second; | |||
if (std::is_same<calyx::AddFNOp, TLibraryOp>::value) | |||
name = "std_addFN"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: isn't the better way to do this is remove the .ieee754
prefix for any F
operations?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The entire prefix + name is calyx.ieee754.add
/calyx.ieee754.mul
, removing ieee754
will simply become add
/mul
; but we want std_addFN
/std_mulFN
after emitting to Calyx native compiler.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
More concretely, I think there is a better way to do this than manually write a new if
statement for each floating point operation. It'd be nice to avoid this every time:
if (std::is_same<calyx::<MyNewFloatingPointOp, TLibraryOP>::value)
name = "std_MyNewFloatingPointOp";
Ideally, since we're already distinguishing them without a _std
prefix, we could infer this is some floating point operation, and then check what standard it conforms too.
/// Returns the technical floating point this operation conforms to, e.g., "ieee754"
/// (This should probably be an enum, which is subsequently converted to a string.)
std::string standard() { ... }
if (auto fOp = dyn_cast<FloatingPointOp>(op)):
name.remove(llvm::join(".", fOp.standard()));
name.prepend("std_");
(I'm not saying you should spend a lot of time doing this, it is a mere suggestion. In general, I do think the conforming technical standard of a floating point operation should be a field of the op so it can referenced during pattern rewrites.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've thought about this, I don't think it's worth it now.
In the foreseeable future, only IEEE754 floating point is integrated; and moreover, only Add and Mul are present in the Berkeley HardFloat.
My opinion is we can address future needs and refactor the code when there is an actual, concrete need.
But I totally get what you are saying.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, let me take it back.
When doing some research on the floating point comparison issue #7821, I found Berkeley HardFloat has division, square root, and comparisons as well.
So I definitely need to use a better design to incorporate all of these calyx::FloatingPointOp
s, as you suggested.
@@ -1019,11 +1024,15 @@ void Emitter::emitLibraryFloatingPoint(Operation *op) { | |||
return; | |||
} | |||
|
|||
StringRef opName = op->getName().getStringRef(); | |||
std::string opName; | |||
if (isa<calyx::AddFNOp>(op)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Still not a fan of this if-else
, it implies that there will only ever exist two FN functions, add and mul.
5e94ef3
to
36de962
Compare
I'm using May not be the best design, happy to hear folks' opinions. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice, definitely a big improvement. A few more comments.
std::string modified = llvm::join_items("", "std_", name, "FN"); | ||
name = modified; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To avoid a potential copy, just use the r-value:
std::string modified = llvm::join_items("", "std_", name, "FN"); | |
name = modified; | |
name = llvm::join_items("", "std_", name, "FN"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think name = llvm::join_items("", "std_", name, "FN");
work
llvm::join_items
returns a temporary std::string
so it cannot directly assign to a StringRef
(name
here). So I had to assign it to a variable modified
before assigning to name
...
Is there any idiomatic way to deal with this, name = StringRef(llvm::join_items("", "std_", name, "FN"))
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh then isn't this a dangling reference since modified
is destroyed when we exit the if
statement? I think the safest course of action is to make name
a std::string.
auto standard = TLibraryOp::getFloatingPointStandard(); | ||
if (standard == FloatingPointStandard::IEEE754) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think a switch would be appropriate here, so that that if someone adds another case the compiler will complain if we don't update this.
auto standard = TLibraryOp::getFloatingPointStandard(); | |
if (standard == FloatingPointStandard::IEEE754) { | |
switch(TLibraryOp::getFloatingPointStandard()) { | |
case FloatingPointStandard::IEEE754: | |
... | |
} |
@@ -1019,11 +1024,15 @@ void Emitter::emitLibraryFloatingPoint(Operation *op) { | |||
return; | |||
} | |||
|
|||
StringRef opName = op->getName().getStringRef(); | |||
std::string opName; | |||
if (isa<calyx::AddFNOp>(op)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Still not a fan of this if-else
, it implies that there will only ever exist two FN functions, add and mul.
IEEE754, | ||
}; | ||
|
||
/// Signals that the following operation operates on floating point values. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/// Signals that the following operation operates on floating point values. | |
/// Signals that the following operation operates on floating point values. | |
/// Unlike other CIRCT dialects, Calyx treats inputs as signless values and | |
/// leaves their interpretation to the respective operation. |
|
||
def AddFNOp : ArithBinaryFloatingPointLibraryOp<"addFN"> { | ||
def AddFNOp : ArithBinaryFloatingPointLibraryOp<"ieee754.add"> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a big improvement. IMO, a better name for this might be: AddFOpIeee754
and similar for below. I imagine future ops would follow similar patterns, e.g., AddFOpBf16
.
I have fixed the |
if constexpr (HasGetFloatingPointStandard<TLibraryOp>::value) { | ||
switch (TLibraryOp::getFloatingPointStandard()) { | ||
case FloatingPointStandard::IEEE754: { | ||
std::string prefix = "ieee754."; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
std::string prefix = "ieee754."; | |
constexpr char[] prefix = "ieee754."; |
case FloatingPointStandard::IEEE754: { | ||
std::string prefix = "ieee754."; | ||
assert(name.find(prefix) == 0 && | ||
"IEEE754 type operation's name must begin with 'ieee754'"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code duplication nit: use prefix
instead of repeating the string ieee754
.
@@ -417,13 +417,37 @@ class ComponentLoweringStateInterface { | |||
} | |||
} | |||
|
|||
template <typename T, typename = void> | |||
struct HasGetFloatingPointStandard : std::false_type {}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggestion: this name is a bit confusing, maybe just IsFloatingPoint
?
…naming for mulf as well as addf to be mulFN/addFN
…h different floating point standards more programmably
1e8adca
to
1e557ba
Compare
thanks for the suggestions @cgyurgyik ! I've also changed |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks good to me. I believe Rachit is on vacation, so I think it would be ok to merge (when CI passes) if this is blocking you, and then address any comments later.
Sounds good, thank you! |
Add support for floating point multiplication in Calyx