diff options
-rw-r--r-- | src/passes/OptimizeInstructions.cpp | 33 | ||||
-rw-r--r-- | test/lit/passes/optimize-instructions-gc-heap.wast | 30 |
2 files changed, 43 insertions, 20 deletions
diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp index 5613aca11..3486fe82a 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -1931,12 +1931,6 @@ struct OptimizeInstructions return false; } - if (new_->isWithDefault()) { - // Ignore a new_default for now. If the fields are defaultable then we - // could add them, in principle, but that might increase code size. - return false; - } - auto index = set->index; auto& operands = new_->operands; @@ -1953,20 +1947,35 @@ struct OptimizeInstructions } // We must move the set's value past indexes greater than it (Y and Z in - // the example in the comment on this function). + // the example in the comment on this function). If this is not with_default + // then we must check for effects. // TODO When this function is called repeatedly in a sequence this can // become quadratic - perhaps we should memoize (though, struct sizes // tend to not be ridiculously large). - for (Index i = index + 1; i < operands.size(); i++) { - auto operandEffects = effects(operands[i]); - if (operandEffects.invalidates(setValueEffects)) { - // TODO: we could use locals to reorder everything - return false; + if (!new_->isWithDefault()) { + for (Index i = index + 1; i < operands.size(); i++) { + auto operandEffects = effects(operands[i]); + if (operandEffects.invalidates(setValueEffects)) { + // TODO: we could use locals to reorder everything + return false; + } } } + // We can optimize here! Builder builder(*getModule()); + // If this was with_default then we add default values now. That does + // increase code size in some cases (if there are many values, and few sets + // that get removed), but in general this optimization is worth it. + if (new_->isWithDefault()) { + auto& fields = new_->type.getHeapType().getStruct().fields; + for (auto& field : fields) { + auto zero = Literal::makeZero(field.type); + operands.push_back(builder.makeConstantExpression(zero)); + } + } + // See if we need to keep the old value. if (effects(operands[index]).hasUnremovableSideEffects()) { operands[index] = diff --git a/test/lit/passes/optimize-instructions-gc-heap.wast b/test/lit/passes/optimize-instructions-gc-heap.wast index 137105468..6db63fae3 100644 --- a/test/lit/passes/optimize-instructions-gc-heap.wast +++ b/test/lit/passes/optimize-instructions-gc-heap.wast @@ -9,10 +9,11 @@ ;; CHECK: (type $struct (struct (field (mut i32)))) (type $struct (struct (field (mut i32)))) + ;; CHECK: (type $struct3 (struct (field (mut i32)) (field (mut i32)) (field (mut i32)))) + ;; CHECK: (type $struct2 (struct (field (mut i32)) (field (mut i32)))) (type $struct2 (struct (field (mut i32)) (field (mut i32)))) - ;; CHECK: (type $struct3 (struct (field (mut i32)) (field (mut i32)) (field (mut i32)))) (type $struct3 (struct (field (mut i32)) (field (mut i32)) (field (mut i32)))) ;; CHECK: (func $tee (type $1) @@ -575,22 +576,35 @@ ;; CHECK: (func $default (type $1) ;; CHECK-NEXT: (local $ref (ref null $struct)) - ;; CHECK-NEXT: (struct.set $struct 0 - ;; CHECK-NEXT: (local.tee $ref - ;; CHECK-NEXT: (struct.new_default $struct) + ;; CHECK-NEXT: (local $ref3 (ref null $struct3)) + ;; CHECK-NEXT: (local.set $ref + ;; CHECK-NEXT: (struct.new $struct + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $ref3 + ;; CHECK-NEXT: (struct.new $struct3 + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: (i32.const 33) + ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (i32.const 20) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $default (local $ref (ref null $struct)) + (local $ref3 (ref null $struct3)) + ;; We optimize new_default as well, adding default values as needed. (struct.set $struct 0 (local.tee $ref - ;; Ignore a new_default for now. If the fields are defaultable then we - ;; could add them, in principle, but that might increase code size. (struct.new_default $struct) ) - (i32.const 20) + (i32.const 10) + ) + (struct.set $struct3 1 + (local.tee $ref3 + (struct.new_default $struct3) + ) + (i32.const 33) ) ) |