Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
b9ca885
work
kripken Apr 4, 2024
b06f76e
work
kripken Apr 5, 2024
a75e5c1
work
kripken Apr 5, 2024
b86fcf3
builds
kripken Apr 5, 2024
3e728e6
todo
kripken Apr 5, 2024
5dfda8d
formt
kripken Apr 5, 2024
336dfcb
work
kripken Apr 5, 2024
fba8353
fix.comment
kripken Apr 5, 2024
dfe819d
typo
kripken Apr 5, 2024
517d02d
fix compiler warnings
kripken Apr 5, 2024
4608b29
exciting
kripken Apr 5, 2024
347ebe0
yolo
kripken Apr 5, 2024
90cabde
yolo
kripken Apr 5, 2024
eef7df5
prep
kripken Apr 5, 2024
86c98ad
work
kripken Apr 5, 2024
a51a7cb
test
kripken Apr 5, 2024
3b44e01
twork
kripken Apr 5, 2024
e21ce34
fix
kripken Apr 5, 2024
2962579
work
kripken Apr 5, 2024
1e01201
work
kripken Apr 5, 2024
6a39f6a
work
kripken Apr 5, 2024
faad72d
work
kripken Apr 5, 2024
d5383ad
work
kripken Apr 5, 2024
bfab495
work
kripken Apr 5, 2024
fd102dd
fix
kripken Apr 5, 2024
0dd4970
work
kripken Apr 5, 2024
4bd7a37
work
kripken Apr 5, 2024
bdb88d6
work
kripken Apr 5, 2024
3a195cf
work
kripken Apr 5, 2024
1b1cc3b
work
kripken Apr 5, 2024
996ab05
work
kripken Apr 5, 2024
7ebb094
test
kripken Apr 5, 2024
00616b8
fix
kripken Apr 5, 2024
e6480d6
format
kripken Apr 5, 2024
bffbdd8
fix
kripken Apr 5, 2024
1c9e1c5
commento
kripken Apr 5, 2024
d11f563
Merge remote-tracking branch 'origin/main' into heap2local.nfc.2
kripken Apr 6, 2024
db6780f
test
kripken Apr 6, 2024
e833122
test
kripken Apr 6, 2024
8a0431a
work
kripken Apr 8, 2024
c793aa8
test
kripken Apr 8, 2024
bfceeb8
work
kripken Apr 8, 2024
9b2be22
test?
kripken Apr 8, 2024
58253c7
fix
kripken Apr 8, 2024
08fe484
undo
kripken Apr 8, 2024
41c0765
nicer
kripken Apr 8, 2024
37de9a5
comment
kripken Apr 8, 2024
c6387b5
test.bad
kripken Apr 8, 2024
82038d3
test.bad
kripken Apr 8, 2024
de4c680
work
kripken Apr 8, 2024
22a18fb
work
kripken Apr 8, 2024
e6b8fcf
work
kripken Apr 8, 2024
606da66
work
kripken Apr 8, 2024
a06ac62
format
kripken Apr 8, 2024
7a57798
Merge remote-tracking branch 'origin/main' into heap2local.nfc.2
kripken Apr 9, 2024
f1e9013
fix
kripken Apr 9, 2024
5c17188
clarify
kripken Apr 9, 2024
becbf61
feedback
kripken Apr 9, 2024
4e279b0
feedback: remove unused code
kripken Apr 9, 2024
568856c
feedback: fix
kripken Apr 9, 2024
83b3b0b
feedback: test
kripken Apr 9, 2024
1e02354
feedback: test
kripken Apr 9, 2024
627da24
Merge branch 'heap2local.nfc.2' into heap2local.3
kripken Apr 9, 2024
560ad8b
Merge remote-tracking branch 'origin/main' into heap2local.3
kripken Apr 9, 2024
4433489
Another test was optimized, it appears
kripken Apr 9, 2024
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 26 additions & 12 deletions src/passes/Heap2Local.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@
// This optimization focuses on such cases.
//

#include "ir/bits.h"
#include "ir/branch-utils.h"
#include "ir/find_all.h"
#include "ir/local-graph.h"
Expand Down Expand Up @@ -648,6 +649,21 @@ struct Struct2Local : PostWalker<Struct2Local> {
curr->finalize();
}

// Add a mask for packed fields. We add masks on sets rather than on gets
// because gets tend to be more numerous both in code appearances and in
// runtime execution. As a result of masking on sets, the value in the local
// is always the masked value (which is also nice for debugging,
// incidentally).
Expression* addMask(Expression* value, const Field& field) {
if (!field.isPacked()) {
return value;
}

auto mask = Bits::lowBitMask(field.getByteSize() * 8);
return builder.makeBinary(
AndInt32, value, builder.makeConst(int32_t(mask)));
}

void visitStructNew(StructNew* curr) {
if (curr != allocation) {
return;
Expand Down Expand Up @@ -693,9 +709,10 @@ struct Struct2Local : PostWalker<Struct2Local> {

// Copy them to the normal ones.
for (Index i = 0; i < tempIndexes.size(); i++) {
contents.push_back(builder.makeLocalSet(
localIndexes[i],
builder.makeLocalGet(tempIndexes[i], fields[i].type)));
auto* value = builder.makeLocalGet(tempIndexes[i], fields[i].type);
// Add a mask on the values we write.
contents.push_back(
builder.makeLocalSet(localIndexes[i], addMask(value, fields[i])));
}

// TODO Check if the nondefault case does not increase code size in some
Expand All @@ -704,8 +721,11 @@ struct Struct2Local : PostWalker<Struct2Local> {
// defaults.
} else {
// Set the default values.
//
// Note that we must assign the defaults because we might be in a loop,
// that is, there might be a previous value.
//
// Note there is no need to mask as these are zeros anyhow.
for (Index i = 0; i < localIndexes.size(); i++) {
contents.push_back(builder.makeLocalSet(
localIndexes[i],
Expand Down Expand Up @@ -766,7 +786,8 @@ struct Struct2Local : PostWalker<Struct2Local> {
// write the data to the local instead of the heap allocation.
replaceCurrent(builder.makeSequence(
builder.makeDrop(curr->ref),
builder.makeLocalSet(localIndexes[curr->index], curr->value)));
builder.makeLocalSet(localIndexes[curr->index],
addMask(curr->value, fields[curr->index]))));
}

void visitStructGet(StructGet* curr) {
Expand Down Expand Up @@ -1111,14 +1132,7 @@ struct Heap2Local {
}

bool canHandleAsLocal(const Field& field) {
if (!TypeUpdating::canHandleAsLocal(field.type)) {
return false;
}
if (field.isPacked()) {
// TODO: support packed fields by adding coercions/truncations.
return false;
}
return true;
return TypeUpdating::canHandleAsLocal(field.type);
Copy link
Member

Choose a reason for hiding this comment

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

FWIW this is always true, and I can't imagine that we would allow a future proposal to make this anything but true.

}

bool canHandleAsLocals(Type type) {
Expand Down
236 changes: 226 additions & 10 deletions test/lit/passes/heap2local.wast
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,17 @@

;; CHECK: (type $6 (func (result anyref)))

;; CHECK: (type $struct.packed (struct (field (mut i8))))
(type $struct.packed (struct (field (mut i8))))
;; CHECK: (type $7 (func (param i32) (result f64)))

;; CHECK: (type $struct.packed (struct (field (mut i8)) (field (mut i32))))
(type $struct.packed (struct (field (mut i8)) (field (mut i32))))

(type $struct.nondefaultable (struct (field (ref $struct.A))))

(type $struct.recursive (struct (field (mut (ref null $struct.recursive)))))

(type $struct.nonnullable (struct (field (ref $struct.A))))

;; CHECK: (type $8 (func (param i32) (result f64)))

;; CHECK: (type $9 (func (param (ref null $struct.recursive))))

;; CHECK: (type $10 (func (param (ref $struct.A))))
Expand Down Expand Up @@ -175,19 +175,114 @@
)

;; CHECK: (func $packed (type $1)
;; CHECK-NEXT: (local $temp (ref $struct.packed))
;; CHECK-NEXT: (local $1 i32)
;; CHECK-NEXT: (local $2 i32)
;; CHECK-NEXT: (local $3 i32)
;; CHECK-NEXT: (local $4 i32)
;; CHECK-NEXT: (local $5 i32)
;; CHECK-NEXT: (local $6 i32)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block (result nullref)
;; CHECK-NEXT: (local.set $3
;; CHECK-NEXT: (i32.const 1337)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $4
;; CHECK-NEXT: (i32.const 1338)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $1
;; CHECK-NEXT: (i32.and
;; CHECK-NEXT: (local.get $3)
;; CHECK-NEXT: (i32.const 255)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $2
;; CHECK-NEXT: (local.get $4)
;; CHECK-NEXT: )
;; CHECK-NEXT: (ref.null none)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (block
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.null none)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $1
;; CHECK-NEXT: (i32.and
;; CHECK-NEXT: (i32.const 99998)
;; CHECK-NEXT: (i32.const 255)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.get_u $struct.packed 0
;; CHECK-NEXT: (struct.new_default $struct.packed)
;; CHECK-NEXT: (block (result i32)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.null none)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.get $1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (block
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.null none)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $2
;; CHECK-NEXT: (i32.const 99999)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block (result i32)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.null none)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.get $2)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block (result nullref)
;; CHECK-NEXT: (local.set $5
;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $6
;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: (ref.null none)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $packed
;; We do not optimize packed structs yet.
(local $temp (ref $struct.packed))
;; Packed fields require masking of irrelevant bits, which we apply on the
;; sets.
(local.set $temp
(struct.new $struct.packed
(i32.const 1337)
(i32.const 1338)
)
)
(struct.set $struct.packed 0
(local.get $temp)
(i32.const 99998)
)
(drop
(struct.get $struct.packed 0
(struct.new_default $struct.packed)
(local.get $temp)
)
)
;; Unpacked fields in the same struct do not need anything.
(struct.set $struct.packed 1
(local.get $temp)
(i32.const 99999)
)
(drop
(struct.get $struct.packed 1
(local.get $temp)
)
)
;; When using struct.new_default we do not need any masking, as the values
;; written are 0 anyhow.
(local.set $temp
(struct.new_default $struct.packed)
)
)

;; CHECK: (func $with-init-values (type $1)
Expand Down Expand Up @@ -647,7 +742,7 @@
)
)

;; CHECK: (func $local-copies-conditional (type $8) (param $x i32) (result f64)
;; CHECK: (func $local-copies-conditional (type $7) (param $x i32) (result f64)
;; CHECK-NEXT: (local $ref (ref null $struct.A))
;; CHECK-NEXT: (local $2 i32)
;; CHECK-NEXT: (local $3 f64)
Expand Down Expand Up @@ -741,7 +836,7 @@
)
)

;; CHECK: (func $non-exclusive-get (type $8) (param $x i32) (result f64)
;; CHECK: (func $non-exclusive-get (type $7) (param $x i32) (result f64)
;; CHECK-NEXT: (local $ref (ref null $struct.A))
;; CHECK-NEXT: (local.set $ref
;; CHECK-NEXT: (struct.new_default $struct.A)
Expand Down Expand Up @@ -3311,3 +3406,124 @@
)
)
)

;; Packed arrays.
(module
;; CHECK: (type $0 (func (result i32)))

;; CHECK: (type $array8 (array (mut i8)))
(type $array8 (array (mut i8)))

;; CHECK: (type $array16 (array (mut i16)))
(type $array16 (array (mut i16)))

;; CHECK: (func $array8 (type $0) (result i32)
;; CHECK-NEXT: (local $temp (ref $array8))
;; CHECK-NEXT: (local $1 i32)
;; CHECK-NEXT: (local $2 i32)
;; CHECK-NEXT: (local $3 i32)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block (result nullref)
;; CHECK-NEXT: (local.set $1
;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $2
;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $3
;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: (ref.null none)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (block
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.null none)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $2
;; CHECK-NEXT: (i32.and
;; CHECK-NEXT: (i32.const 1337)
;; CHECK-NEXT: (i32.const 255)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (block (result i32)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.null none)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.get $2)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $array8 (result i32)
(local $temp (ref $array8))
(local.set $temp
(array.new_default $array8
(i32.const 3)
)
)
(array.set $array8
(local.get $temp)
(i32.const 1)
(i32.const 1337)
)
(array.get $array8
(local.get $temp)
(i32.const 1)
)
)

;; CHECK: (func $array16 (type $0) (result i32)
;; CHECK-NEXT: (local $temp (ref $array16))
;; CHECK-NEXT: (local $1 i32)
;; CHECK-NEXT: (local $2 i32)
;; CHECK-NEXT: (local $3 i32)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block (result nullref)
;; CHECK-NEXT: (local.set $1
;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $2
;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $3
;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: (ref.null none)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (block
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.null none)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $2
;; CHECK-NEXT: (i32.and
;; CHECK-NEXT: (i32.const 1337)
;; CHECK-NEXT: (i32.const 65535)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (block (result i32)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.null none)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.get $2)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $array16 (result i32)
(local $temp (ref $array16))
(local.set $temp
(array.new_default $array16
(i32.const 3)
)
)
(array.set $array16
(local.get $temp)
(i32.const 1)
(i32.const 1337)
)
(array.get $array16
(local.get $temp)
(i32.const 1)
)
)
)
Loading