diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/ir/gc-type-utils.h | 11 | ||||
-rw-r--r-- | src/passes/OptimizeInstructions.cpp | 35 |
2 files changed, 34 insertions, 12 deletions
diff --git a/src/ir/gc-type-utils.h b/src/ir/gc-type-utils.h index 2d3783591..1fecca22e 100644 --- a/src/ir/gc-type-utils.h +++ b/src/ir/gc-type-utils.h @@ -70,18 +70,21 @@ inline EvaluationResult evaluateCastCheck(Type refType, Type castType) { auto refHeapType = refType.getHeapType(); auto castHeapType = castType.getHeapType(); + auto refIsHeapSubType = HeapType::isSubType(refHeapType, castHeapType); auto castIsHeapSubType = HeapType::isSubType(castHeapType, refHeapType); bool heapTypesCompatible = refIsHeapSubType || castIsHeapSubType; - if (!heapTypesCompatible) { - // If at least one is not null, then since the heap types are not compatible - // we must fail. + if (!heapTypesCompatible || castHeapType.isBottom()) { + // If the heap types are incompatible or if it is impossible to have a + // non-null reference to the target heap type, then the only way the cast + // can succeed is if it allows nulls and the input is null. if (refType.isNonNullable() || castType.isNonNullable()) { return Failure; } - // Otherwise, both are nullable and a null is the only hope of success. + // Both are nullable. A null is the only hope of success in either + // situation. return SuccessOnlyIfNull; } diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp index 5d91ce29f..c9e1ced19 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -2029,14 +2029,33 @@ struct OptimizeInstructions curr->type)); return; } else if (result == GCTypeUtils::SuccessOnlyIfNull) { - curr->type = Type(nullType, Nullable); - // Call replaceCurrent() to make us re-optimize this node, as we may - // have just unlocked further opportunities. (We could just continue - // down to the rest, but we'd need to do more work to make sure all - // the local state in this function is in sync which this change; it's - // easier to just do another clean pass on this node.) - replaceCurrent(curr); - return; + // If either cast or ref types were non-nullable then the cast could + // never succeed, and we'd have reached |Failure|, above. + assert(curr->type.isNullable() && curr->ref->type.isNullable()); + + // The cast either returns null, or traps. In trapsNeverHappen mode + // we know the result, since it by assumption will not trap. + if (getPassOptions().trapsNeverHappen) { + replaceCurrent(builder.makeBlock( + {builder.makeDrop(curr->ref), builder.makeRefNull(nullType)}, + curr->type)); + return; + } + + // Without trapsNeverHappen we can at least sharpen the type here, if + // it is not already a null type. + auto newType = Type(nullType, Nullable); + if (curr->type != newType) { + curr->type = newType; + // Call replaceCurrent() to make us re-optimize this node, as we + // may have just unlocked further opportunities. (We could just + // continue down to the rest, but we'd need to do more work to + // make sure all the local state in this function is in sync + // which this change; it's easier to just do another clean pass + // on this node.) + replaceCurrent(curr); + return; + } } auto** last = refp; |