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

replace old style union in initializing expression representation with std::variant #1828

Closed

Conversation

dbezhetskov
Copy link
Contributor

Hi, this is just small cleanup.
I've replaced union with std::variant to free Type type because now it should be always POD as a member of union.
Suddenly, after refactoring I revealed that FuncRef and NullRef cases and Type actually not used in our code so I don't move them into new version of InitExpr.

Copy link
Member

@sbc100 sbc100 left a comment

Choose a reason for hiding this comment

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

I've heard that using any/optional/variant could be problematic for macOS < 10.13... but I'm not sure if wabt has uses who care about that.

FuncRef,
// TODO: There isn't a nullref anymore, this just represents ref.null of some
// type T.
NullRef,
Copy link
Member

Choose a reason for hiding this comment

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

I wonder if that fact that FuncRef and NullRef are undefined actually just about us missing test cases for them. I guess it doesn't hurt to land this either way.

@takikawa
Copy link
Contributor

takikawa commented Apr 5, 2022

Are there any remaining issues with landing this? It would be helpful for completing the implementation of typed funcrefs and also GC support, as it would allow including a Var or similar data in the type.

@takikawa
Copy link
Contributor

takikawa commented Apr 5, 2022

Are there any remaining issues with landing this? It would be helpful for completing the implementation of typed funcrefs and also GC support, as it would allow including a Var or similar data in the type.

It looks like this actually has to be rebased to be mergeable now, since the relevant file has been updated recently. I can work on rebasing this

@takikawa
Copy link
Contributor

takikawa commented Apr 5, 2022

I've rebased this now on this branch takikawa@9048277

@dbezhetskov could you update your PR branch with this commit and re-push? Thanks.

@dbezhetskov
Copy link
Contributor Author

I've updated the branch, thanks @takikawa

v128 v128_v;
Type type;
} imm;
std::variant<I32Value, I64Value, F32Value, F64Value, v128, GlobalIndex> data;
Copy link
Member

Choose a reason for hiding this comment

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

Why not continue to call this imm which seems more descriptive?

Also, maybe we can call all the possible variantes accordingly: ImmI32, ImmI64, ImmF64, ImmV128, ImmIndex.

Copy link
Member

Choose a reason for hiding this comment

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

Also can we avoid all the .value accessors below if we use typedefs here instead of nested structs:

typedef ImmI32 uint32_t;

?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks for the comments Sam!
I've returned imm name and renamed structs.

Actually I can't reduce .value because in that case variant will have equal types. For example, we represent i64 and f64 as uint64_t. In that case we can't use std::visit and std::get<Type> because variant's options aren't unique, so one possible solution is to use indices:

using ImmI32 = uint32_t;
using ImmI64 = uint64_t;
using ImmF32 = uint32_t;
using ImmF64 = uint64_t;
using ImmV128 = v128;
using ImmIndex = Index;

constexpr std::size_t ImmI32TypeIdx = 0;
constexpr std::size_t ImmI64TypeIdx = 1;
constexpr std::size_t ImmF32TypeIdx = 2;
constexpr std::size_t ImmF64TypeIdx = 3;
constexpr std::size_t ImmV128TypeIdx = 4;
constexpr std::size_t ImmIndexTypeIdx = 5;

For that case we should manually keep them in sync with the order of types in the variant.
Moreover, beautiful std::visit for this case can't be used and then should be replaced with a chain of if-then-else, so I think structs are ok, wdyt?

Copy link
Member

Choose a reason for hiding this comment

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

I don't understand the "variant's options aren't unique" part? Aren't different typedefs considered unique types?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I mean when we have typedefs or usings like this:

using ImmI32 = uint32_t;
using ImmI64 = uint64_t;
using ImmF32 = uint32_t;
using ImmF64 = uint64_t;
using ImmV128 = v128;
using ImmIndex = Index;

then std::variant<ImmI32, ImmI64, ImmF32, ImmF64, ImmV128, ImmIndex> translates into
std::variant<uint32_t, uint64_t, uint32_t, uint64_t, v128, Index>.
So, we can't use std::get<ImmI32> because it translates into std::get<uint32_t> because using/typedef doesn't introduce new types, it is just a type alias. In the end, std::get<uint32_t> is ambiguous because it can refer to a place of ImmI32 or refer to a place of ImmF32.

The documentations for std::get says: The call is ill-formed if T is not a unique element of Types....

Copy link
Member

Choose a reason for hiding this comment

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

In that case maybe we can reduce the variant to just ImmU32, ImmU64 and ImmV128?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Actually we can, but in that case PrintInitExpr(const InitExpr& expr) can't distinguish between F64 and I64 without any additional enum opcode support (we removed it in this PR).

The main argument here is that we replace the (union + enum opcode) scheme with a single std::variant because it is more convenient to add new types and compiler can check that some options in std::visit are uncovered.

I don't see any memory or speed degradation from this change, at least in theory, because std::variant will have the same size as the previous union based solution with enum class InitExprType. With variant we just don't need to manually maintain InitExprType.

Copy link
Member

@binji binji left a comment

Choose a reason for hiding this comment

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

This seems like a smart change for the reasons @takikawa mentioned. I personally don't really like using std::visit and overloaded very much since it is a little complex, and would prefer using a switch/index instead. But it's mostly just a personal opinion, and I see that using overloaded does provide better type safety.

@sbc100
Copy link
Member

sbc100 commented Apr 18, 2022

This seems like a smart change for the reasons @takikawa mentioned. I personally don't really like using std::visit and overloaded very much since it is a little complex, and would prefer using a switch/index instead. But it's mostly just a personal opinion, and I see that using overloaded does provide better type safety.

I think I agree. I prefer the old switch style. Especially if that allows use to avoid adding all those new struct types.

@keithw
Copy link
Member

keithw commented Feb 9, 2023

In the interests of trying to clean up the PR backlog -- is there still interest in pursuing this (subject to comments) or is this ok to close?

@takikawa
Copy link
Contributor

takikawa commented Feb 9, 2023

Hi @keithw, thanks for checking in. I personally don't have the bandwidth to pursue this currently and am ok with closing it. WDYT @dbezhetskov?

@dbezhetskov
Copy link
Contributor Author

The same for me, feel free to close this.

@keithw
Copy link
Member

keithw commented Feb 10, 2023

Thanks for the quick responses! OK, closing for now.

@keithw keithw closed this Feb 10, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants