diff --git a/lib/Optimizer/Scalar/FunctionAnalysis.cpp b/lib/Optimizer/Scalar/FunctionAnalysis.cpp index 6c331d24fc9..4f52f31ed96 100644 --- a/lib/Optimizer/Scalar/FunctionAnalysis.cpp +++ b/lib/Optimizer/Scalar/FunctionAnalysis.cpp @@ -139,6 +139,11 @@ void analyzeCreateCallable(BaseCreateCallableInst *create) { Module *M = F->getParent(); IRBuilder builder(M); + // The only kind of function which does not expect a made `this` parameter in + // a construct call is a derived class constructor. + bool funcExpectsThisInConstruct = + F->getDefinitionKind() != Function::DefinitionKind::ES6DerivedConstructor; + /// Define an element in the worklist below. struct UserInfo { /// An instruction that is known to have either the value of the closure, or @@ -170,6 +175,8 @@ void analyzeCreateCallable(BaseCreateCallableInst *create) { // to avoid going back and forth between the corresponding loads. llvh::SmallPtrSet visited{}; + IRBuilder::InstructionDestroyer destroyer{}; + worklist.push_back({create, true, create->getScope()}); while (!worklist.empty()) { // Instruction whose only possible closure value is the one being analyzed. @@ -202,6 +209,17 @@ void analyzeCreateCallable(BaseCreateCallableInst *create) { assert( CTI->getClosure() == closureInst && "Closure must be closure argument to CreateThisInst"); + if (isAlwaysClosure) { + if (funcExpectsThisInConstruct) { + CTI->setType(Type::createObject()); + } else { + // If a function does not expect any `this`, then we can just remove + // the `CreateThis` altogether since it was just going to return + // undefined. + CTI->replaceAllUsesWith(builder.getLiteralUndefined()); + destroyer.add(CTI); + } + } // CreateThis leaks the closure because the created object can still // access the function via its parent's `.constructor` prototype. F->getAttributesRef(M)._allCallsitesKnownInStrictMode = false; diff --git a/test/Optimizer/cache-new-object-analysis.js b/test/Optimizer/cache-new-object-analysis.js index 147c828bc52..31349916fb5 100644 --- a/test/Optimizer/cache-new-object-analysis.js +++ b/test/Optimizer/cache-new-object-analysis.js @@ -36,10 +36,9 @@ return new simple(1, 2); // CHECK-NEXT:%BB0: // CHECK-NEXT: %0 = GetParentScopeInst (:environment) %VS0: any, %parentScope: environment // CHECK-NEXT: %1 = CreateFunctionInst (:object) %0: environment, %simple(): functionCode -// CHECK-NEXT: %2 = CreateThisInst (:undefined|object) %1: object, empty: any -// CHECK-NEXT: %3 = CallInst (:undefined) %1: object, %simple(): functionCode, true: boolean, empty: any, undefined: undefined, %2: undefined|object, 1: number, 2: number -// CHECK-NEXT: %4 = GetConstructedObjectInst (:object) %2: undefined|object, undefined: undefined -// CHECK-NEXT: ReturnInst %4: object +// CHECK-NEXT: %2 = CreateThisInst (:object) %1: object, empty: any +// CHECK-NEXT: %3 = CallInst (:undefined) %1: object, %simple(): functionCode, true: boolean, empty: any, undefined: undefined, %2: object, 1: number, 2: number +// CHECK-NEXT: ReturnInst %2: object // CHECK-NEXT:function_end // CHECK:function simple(x: any, y: any): undefined diff --git a/test/Optimizer/callee.js b/test/Optimizer/callee.js index 35974204132..f86095c5cbd 100644 --- a/test/Optimizer/callee.js +++ b/test/Optimizer/callee.js @@ -80,10 +80,9 @@ function load_store_multiple_test() { // CHECK-NEXT:%BB0: // CHECK-NEXT: %0 = GetParentScopeInst (:environment) %VS0: any, %parentScope: environment // CHECK-NEXT: %1 = CreateFunctionInst (:object) %0: environment, %"foo 1#"(): functionCode -// CHECK-NEXT: %2 = CreateThisInst (:undefined|object) %1: object, empty: any -// CHECK-NEXT: %3 = CallInst (:any) %1: object, %"foo 1#"(): functionCode, true: boolean, empty: any, undefined: undefined, %2: undefined|object, 12: number -// CHECK-NEXT: %4 = GetConstructedObjectInst (:object) %2: undefined|object, 12: number -// CHECK-NEXT: ReturnInst %4: object +// CHECK-NEXT: %2 = CreateThisInst (:object) %1: object, empty: any +// CHECK-NEXT: %3 = CallInst (:any) %1: object, %"foo 1#"(): functionCode, true: boolean, empty: any, undefined: undefined, %2: object, 12: number +// CHECK-NEXT: ReturnInst %2: object // CHECK-NEXT:function_end // CHECK:scope %VS1 [k: object] diff --git a/test/Optimizer/constructor_callee.js b/test/Optimizer/constructor_callee.js index f938076349e..02973bd8783 100644 --- a/test/Optimizer/constructor_callee.js +++ b/test/Optimizer/constructor_callee.js @@ -50,10 +50,9 @@ function ctor_load_store_test() { // CHECK-NEXT:%BB0: // CHECK-NEXT: %0 = GetParentScopeInst (:environment) %VS0: any, %parentScope: environment // CHECK-NEXT: %1 = CreateFunctionInst (:object) %0: environment, %use_this(): functionCode -// CHECK-NEXT: %2 = CreateThisInst (:undefined|object) %1: object, empty: any -// CHECK-NEXT: %3 = CallInst (:any) %1: object, %use_this(): functionCode, true: boolean, empty: any, undefined: undefined, %2: undefined|object, 12: number -// CHECK-NEXT: %4 = GetConstructedObjectInst (:object) %2: undefined|object, %2: undefined|object -// CHECK-NEXT: ReturnInst %4: object +// CHECK-NEXT: %2 = CreateThisInst (:object) %1: object, empty: any +// CHECK-NEXT: %3 = CallInst (:any) %1: object, %use_this(): functionCode, true: boolean, empty: any, undefined: undefined, %2: object, 12: number +// CHECK-NEXT: ReturnInst %2: object // CHECK-NEXT:function_end // CHECK:scope %VS1 [use_this: object] @@ -89,8 +88,7 @@ function ctor_load_store_test() { // CHECK-NEXT:%BB0: // CHECK-NEXT: %0 = GetParentScopeInst (:environment) %VS1: any, %parentScope: environment // CHECK-NEXT: %1 = LoadFrameInst (:object) %0: environment, [%VS1.use_this]: object -// CHECK-NEXT: %2 = CreateThisInst (:undefined|object) %1: object, empty: any -// CHECK-NEXT: %3 = CallInst (:undefined) %1: object, %"use_this 1#"(): functionCode, true: boolean, empty: any, undefined: undefined, %2: undefined|object, 12: number -// CHECK-NEXT: %4 = GetConstructedObjectInst (:object) %2: undefined|object, undefined: undefined -// CHECK-NEXT: ReturnInst %4: object +// CHECK-NEXT: %2 = CreateThisInst (:object) %1: object, empty: any +// CHECK-NEXT: %3 = CallInst (:undefined) %1: object, %"use_this 1#"(): functionCode, true: boolean, empty: any, undefined: undefined, %2: object, 12: number +// CHECK-NEXT: ReturnInst %2: object // CHECK-NEXT:function_end diff --git a/test/Optimizer/constructor_callee_regexp.js b/test/Optimizer/constructor_callee_regexp.js index 375b44b9882..bd006dafadb 100644 --- a/test/Optimizer/constructor_callee_regexp.js +++ b/test/Optimizer/constructor_callee_regexp.js @@ -35,8 +35,8 @@ function ctor_this_test() { // CHECK-NEXT:%BB0: // CHECK-NEXT: %0 = GetParentScopeInst (:environment) %VS0: any, %parentScope: environment // CHECK-NEXT: %1 = CreateFunctionInst (:object) %0: environment, %use_this(): functionCode -// CHECK-NEXT: %2 = CreateThisInst (:undefined|object) %1: object, empty: any -// CHECK-NEXT: %3 = CallInst (:object) %1: object, %use_this(): functionCode, true: boolean, empty: any, undefined: undefined, %2: undefined|object, 12: number +// CHECK-NEXT: %2 = CreateThisInst (:object) %1: object, empty: any +// CHECK-NEXT: %3 = CallInst (:object) %1: object, %use_this(): functionCode, true: boolean, empty: any, undefined: undefined, %2: object, 12: number // CHECK-NEXT: ReturnInst %3: object // CHECK-NEXT:function_end diff --git a/test/Optimizer/function-analysis-construction-store.js b/test/Optimizer/function-analysis-construction-store.js index bb27b99742c..e9fa118a8af 100644 --- a/test/Optimizer/function-analysis-construction-store.js +++ b/test/Optimizer/function-analysis-construction-store.js @@ -33,10 +33,9 @@ function main() { // CHECK-NEXT:%BB0: // CHECK-NEXT: %0 = GetParentScopeInst (:environment) %VS0: any, %parentScope: environment // CHECK-NEXT: %1 = CreateFunctionInst (:object) %0: environment, %f(): functionCode -// CHECK-NEXT: %2 = CreateThisInst (:undefined|object) %1: object, empty: any -// CHECK-NEXT: %3 = CallInst (:undefined) %1: object, %f(): functionCode, true: boolean, empty: any, undefined: undefined, %2: undefined|object -// CHECK-NEXT: %4 = GetConstructedObjectInst (:object) %2: undefined|object, undefined: undefined -// CHECK-NEXT: ReturnInst %4: object +// CHECK-NEXT: %2 = CreateThisInst (:object) %1: object, empty: any +// CHECK-NEXT: %3 = CallInst (:undefined) %1: object, %f(): functionCode, true: boolean, empty: any, undefined: undefined, %2: object +// CHECK-NEXT: ReturnInst %2: object // CHECK-NEXT:function_end // CHECK:function f(): undefined diff --git a/test/Optimizer/function-analysis-construction.js b/test/Optimizer/function-analysis-construction.js index f266a0cfab4..f21512cb03a 100644 --- a/test/Optimizer/function-analysis-construction.js +++ b/test/Optimizer/function-analysis-construction.js @@ -32,10 +32,9 @@ function main() { // CHECK-NEXT:%BB0: // CHECK-NEXT: %0 = GetParentScopeInst (:environment) %VS0: any, %parentScope: environment // CHECK-NEXT: %1 = CreateFunctionInst (:object) %0: environment, %f(): functionCode -// CHECK-NEXT: %2 = CreateThisInst (:undefined|object) %1: object, empty: any -// CHECK-NEXT: %3 = CallInst (:undefined) %1: object, %f(): functionCode, true: boolean, empty: any, undefined: undefined, %2: undefined|object -// CHECK-NEXT: %4 = GetConstructedObjectInst (:object) %2: undefined|object, undefined: undefined -// CHECK-NEXT: ReturnInst %4: object +// CHECK-NEXT: %2 = CreateThisInst (:object) %1: object, empty: any +// CHECK-NEXT: %3 = CallInst (:undefined) %1: object, %f(): functionCode, true: boolean, empty: any, undefined: undefined, %2: object +// CHECK-NEXT: ReturnInst %2: object // CHECK-NEXT:function_end // CHECK:function f(): undefined diff --git a/test/Optimizer/function-analysis-new-target.js b/test/Optimizer/function-analysis-new-target.js index b085954656a..fff36e333d5 100644 --- a/test/Optimizer/function-analysis-new-target.js +++ b/test/Optimizer/function-analysis-new-target.js @@ -35,10 +35,9 @@ function main() { // CHECK-NEXT:%BB0: // CHECK-NEXT: %0 = GetParentScopeInst (:environment) %VS0: any, %parentScope: environment // CHECK-NEXT: %1 = CreateFunctionInst (:object) %0: environment, %f(): functionCode -// CHECK-NEXT: %2 = CreateThisInst (:undefined|object) %1: object, empty: any -// CHECK-NEXT: %3 = CallInst (:undefined) %1: object, %f(): functionCode, true: boolean, empty: any, %1: object, %2: undefined|object -// CHECK-NEXT: %4 = GetConstructedObjectInst (:object) %2: undefined|object, undefined: undefined -// CHECK-NEXT: ReturnInst %4: object +// CHECK-NEXT: %2 = CreateThisInst (:object) %1: object, empty: any +// CHECK-NEXT: %3 = CallInst (:undefined) %1: object, %f(): functionCode, true: boolean, empty: any, %1: object, %2: object +// CHECK-NEXT: ReturnInst %2: object // CHECK-NEXT:function_end // CHECK:function f(): undefined diff --git a/test/Optimizer/inline-new-target.js b/test/Optimizer/inline-new-target.js index f2d76930684..cd47f92cb10 100644 --- a/test/Optimizer/inline-new-target.js +++ b/test/Optimizer/inline-new-target.js @@ -61,8 +61,8 @@ function outer2(){ // CHECK-NEXT: %2 = CreateFunctionInst (:object) %1: environment, %foo(): functionCode // CHECK-NEXT: StoreFrameInst %1: environment, %2: object, [%VS1.foo]: object // CHECK-NEXT: %4 = CreateFunctionInst (:object) %1: environment, %bar(): functionCode -// CHECK-NEXT: %5 = CreateThisInst (:undefined|object) %4: object, empty: any -// CHECK-NEXT: %6 = CallInst (:object) %4: object, %bar(): functionCode, true: boolean, %1: environment, undefined: undefined, %5: undefined|object +// CHECK-NEXT: %5 = CreateThisInst (:object) %4: object, empty: any +// CHECK-NEXT: %6 = CallInst (:object) %4: object, %bar(): functionCode, true: boolean, %1: environment, undefined: undefined, %5: object // CHECK-NEXT: ReturnInst %6: object // CHECK-NEXT:function_end @@ -76,6 +76,6 @@ function outer2(){ // CHECK-NEXT:%BB0: // CHECK-NEXT: %0 = GetParentScopeInst (:environment) %VS1: any, %parentScope: environment // CHECK-NEXT: %1 = LoadFrameInst (:object) %0: environment, [%VS1.foo]: object -// CHECK-NEXT: %2 = CreateThisInst (:undefined|object) %1: object, empty: any +// CHECK-NEXT: %2 = CreateThisInst (:object) %1: object, empty: any // CHECK-NEXT: ReturnInst %1: object // CHECK-NEXT:function_end diff --git a/test/Optimizer/inline-sibling-constructors.js b/test/Optimizer/inline-sibling-constructors.js index ca504226f6c..7920c618acd 100644 --- a/test/Optimizer/inline-sibling-constructors.js +++ b/test/Optimizer/inline-sibling-constructors.js @@ -66,10 +66,9 @@ function outer() { // CHECK-NEXT: %2 = LoadParamInst (:any) %y: any // CHECK-NEXT: %3 = LoadParamInst (:any) %z: any // CHECK-NEXT: %4 = LoadFrameInst (:object) %0: environment, [%VS1.Point]: object -// CHECK-NEXT: %5 = CreateThisInst (:undefined|object) %4: object, empty: any -// CHECK-NEXT: StorePropertyStrictInst %1: any, %5: undefined|object, "x": string -// CHECK-NEXT: StorePropertyStrictInst %2: any, %5: undefined|object, "y": string -// CHECK-NEXT: StorePropertyStrictInst %3: any, %5: undefined|object, "z": string -// CHECK-NEXT: %9 = GetConstructedObjectInst (:object) %5: undefined|object, undefined: undefined -// CHECK-NEXT: ReturnInst %9: object +// CHECK-NEXT: %5 = CreateThisInst (:object) %4: object, empty: any +// CHECK-NEXT: StorePropertyStrictInst %1: any, %5: object, "x": string +// CHECK-NEXT: StorePropertyStrictInst %2: any, %5: object, "y": string +// CHECK-NEXT: StorePropertyStrictInst %3: any, %5: object, "z": string +// CHECK-NEXT: ReturnInst %5: object // CHECK-NEXT:function_end diff --git a/test/Optimizer/regress-construct.js b/test/Optimizer/regress-construct.js index 3875f609a58..7162127675a 100644 --- a/test/Optimizer/regress-construct.js +++ b/test/Optimizer/regress-construct.js @@ -58,11 +58,10 @@ function main() { // CHECK-NEXT: CondBranchInst %1: null|object, %BB2, %BB1 // CHECK-NEXT:%BB1: // CHECK-NEXT: %3 = LoadFrameInst (:object) %0: environment, [%VS1.y]: object -// CHECK-NEXT: %4 = CreateThisInst (:undefined|object) %3: object, empty: any -// CHECK-NEXT: %5 = GetConstructedObjectInst (:object) %4: undefined|object, undefined: undefined -// CHECK-NEXT: StoreFrameInst %0: environment, %5: object, [%VS1.x]: null|object +// CHECK-NEXT: %4 = CreateThisInst (:object) %3: object, empty: any +// CHECK-NEXT: StoreFrameInst %0: environment, %4: object, [%VS1.x]: null|object // CHECK-NEXT: BranchInst %BB2 // CHECK-NEXT:%BB2: -// CHECK-NEXT: %8 = PhiInst (:null|object) %1: null|object, %BB0, %5: object, %BB1 -// CHECK-NEXT: ReturnInst %8: null|object +// CHECK-NEXT: %7 = PhiInst (:null|object) %1: null|object, %BB0, %4: object, %BB1 +// CHECK-NEXT: ReturnInst %7: null|object // CHECK-NEXT:function_end