-
Notifications
You must be signed in to change notification settings - Fork 38
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
Value type improvements #423
Conversation
macOS builds failed |
It looks on macOS the |
Codecov Report
@@ Coverage Diff @@
## master #423 +/- ##
=======================================
Coverage 99.54% 99.54%
=======================================
Files 51 51
Lines 14632 14663 +31
=======================================
+ Hits 14565 14596 +31
Misses 67 67 |
246fdf8
to
dc41d64
Compare
df0ceb4
to
399b2c3
Compare
dc41d64
to
068f218
Compare
using T = decltype(op(stack.top(), stack.top())); | ||
const auto val2 = static_cast<T>(stack.pop()); | ||
const auto val1 = static_cast<T>(stack.top()); | ||
stack.top() = static_cast<std::make_unsigned_t<T>>(op(val1, val2)); |
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.
How come make_unsigned
is not needed anymore? I think it was need for signed 32-bit operations, where without it it would incorrectly extend the sign to 64 bit.
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 I think now constructor Value(int32_t v)
does this.
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.
Is it better to have it in the constructor or make it more explicit here? From a reader's perspective explicitness here is nice.
Do we recall the test case while it had to be converted between signed/unsigned in this function?
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.
All "signed integer" instructions relay on this.
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 I like it better when Value
itself encapsulates correct conversion int32_t
->Value
.
make_unsigned_t
here looked to me more like a hack to handle an edge case...
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.
We could make the conversions from signed integers explicit. This is manageable in interpreter implementation, but inconvenient in tests and examples. I.e. all invokes with example {0, 1, 2}
stops working (as naked integer literal is of type int
) and you have to use {0u, 1u, 2u}
.
Personally, I would leave it as is for now. And we can review Value
type design after having floating point type in. And possibly i32
fields as well.
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'm fine keeping it this way but would prefer a simple comment that the result can be signed or unsigned and its conversion is handled in Value.
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.
Added comments and used Value
explicitly (no implicit conversion although available).
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.
Hope it did not change runtimes lol.
@@ -13,8 +13,19 @@ union Value | |||
uint64_t i64; | |||
|
|||
Value() = default; | |||
constexpr Value(uint64_t v) noexcept : i64{v} {} | |||
constexpr Value(unsigned int v) noexcept : i64{v} {} |
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.
Maybe a comment why we need these unsigned
constuctors?
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.
Added.
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.
Instead of having 5 constructor for ints + deleted constructors, I can do all of that with a template constructor with some enable_if and some other magic. But I'm not sure we want that now. Maybe it's better to wait for floats before more cleanups.
54cd311
to
21a8bbe
Compare
const auto status = | ||
fizzy::execute(*m_instance, static_cast<uint32_t>(func_ref), {first_arg, args.size()}); | ||
const auto status = fizzy::execute( | ||
*m_instance, static_cast<uint32_t>(func_ref), span<const Value>(first_arg, args.size())); |
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.
Is this change required?
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.
Yes. Otherwise compiler thinks these are two values: first_arg
-> bool
-> Value
and args.size()
-> size_t
-> Value
.
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 see. Fo first_arg it's probably pointer to integer conversion, rather than bool
, I think bool
-> Value
doesn't work anymore, you deleted the tests for it.
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.
Without the change it resolves overloading to Value(bool)
what gives the compilation error (as this constructor is deleted).
@@ -1014,7 +1014,7 @@ ExecutionResult execute(Instance& instance, FuncIdx func_idx, span<const Value> | |||
case Instr::i32_eqz: | |||
{ | |||
const auto value = static_cast<uint32_t>(stack.pop()); | |||
stack.push(value == 0); | |||
stack.push(uint32_t{value == 0}); |
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.
Why do we need to make this clear? I know eqz returns 32-bit but we said we don't care because it is a union.
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.
Does this have any effect on speed?
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 Value(bool)
is deleted to avoid some surprises. So the bool expression must be converted to uint32_t
(or uint64_t
, but uint32_t
seems nicer).
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.
Does this have any effect on speed?
Nope.
21a8bbe
to
d90f329
Compare
Cleaned up the git history. |
Benchmarks (no change):
|
const auto val2 = static_cast<T>(stack.pop()); | ||
const auto val1 = static_cast<T>(stack.top()); | ||
stack.top() = static_cast<std::make_unsigned_t<T>>(op(val1, val2)); | ||
using T = decltype(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.
Hm, what does this do with {}, {}
?
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 {}
in f(T)
more/less mean "construct the required type T with default constructor".
E.g. The void func(void*, Options, vector<int>, int)
can be invoked as func(nullptr, {}, {}, 0)
or even func({}, {}, {}, {})
.
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.
Yeah I get that, but in the case of op
, which is std::add
, std::sub
, etc.
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.
Well, op
is an instance of e.g. std::add<uint64_t>{}
which has operator()
overloaded so op(...)
expression is valid. The decltype
gets the type of this expression.
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.
Okay so this line change is not related to the current PR. Could have been like this prior.
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.
Yes. The {}
could have been use instead of stack.top()
before. But now this simplifications is required as stack.top()
has wrong type in general.
d90f329
to
accd4b2
Compare
Depends on #419