-
Notifications
You must be signed in to change notification settings - Fork 694
Description
The current AstSemantics is somewhat baroque and inconsistent regarding loads and stores:
- Both loads and stores specify the type twice, once as part of the local type and once as part of the memory type. This is redundant, because they can only differ in size.
- Loads and stores duplicate the semantics of various conversion operators. It leads to a bit of an inflation of opcodes and is redundant because most can already be expressed by suitable compositions (and the cases where these combinations are needed are probably not very common either).
- At the same time, some combinations are missing. See e.g. issue Extending FP loads, truncating FP stores #316 .
- The load instruction for globals does not allow to choose sign extension behaviour.
The way I see it, there would be two possible approaches for a more consistent design:
1. Loads and stores with memory types, but minimal set of conversions
In this approach,
- MemoryType is an extension of LocalType with additional small sizes.
- Globals have a memory type.
- Memory loads and stores have a memory type.
- The only conversions built into loads and stores are for memory types that are not local types. Access at these types always convert to/from the smallest possible local type.
- Thus, the local type is always uniquely determined by the memory type and doesn't need an extra annotation.
That is, there would be opcodes
int8.load_s/u ;; results in an int32
int16.load_s/u ;; results in an int32
int32.load
int64.load
float32.load
float64.load
load_global_s/u ;; for globals of type int8/16, results in int32
load_global ;; for all others
int8.store ;; takes an int32 and truncates
int16.store ;; takes an int32 and truncates
int32.store
int64.store
float32.store
float64.store
store_global ;; truncates for globals of type int8/16
In the (presumably rarer case) that other combinations are needed, the respective conversion operators are readily available.
2. Loads and stores with local types + memory sizes, all conversions built-in
This is a variation of what was discussed in #82.
In this approach
- There are no memory types.
- Globals are declared with a local type and a size.
- Memory loads and stores have a local type and a size.
- In both cases, the size has to be smaller or equal to the local type's natural size
- The access performs all necessary extensions or truncations.
The opcodes would be
int32.load8_s/u
int32.load16_s/u
int32.load32
int64.load8_s/u
int64.load16_s/u
int64.load32_s/u
int64.load64
float32.load32
float64.load32
float64.load64
load_global_s/u ;; if the global's size is smaller than its type's natural size
load_global ;; for all others
int32.store8
int32.store16
int32.store32
int64.store8
int64.store16
int64.store32
int64.store64
float32.store32
float64.store32
float64.store64
store_global
This scheme requires more opcodes, but saves extra use of conversions.
Additional types
In both schemes it would be easy to add support for additional types, e.g. a float16 memory type. In approach 1, it would introduce opcodes
float16.load ;; extends to float32
float16.store ;; truncates a float32
In approach 2:
float32.load16
float64.load16
float32.store16
float64.store16
Comparison
Approach 1 needs fewer opcodes (especially when adding more types) and has less redundancy with conversion operators. Approach 2 is a bit more explicit about the sizes and closer to what is currently in AstSemantics. Personally, I think apporach 1 is more attractive and less bloated.