Browse Source

chore: [29-x-y] cherry-pick 7 changes from 3-M125 (#42589)

Pedro Pontes 10 months ago
parent
commit
fe869081b3

+ 2 - 0
patches/DirectXShaderCompiler/.patches

@@ -3,3 +3,5 @@ cherry-pick-bc18aec94c82.patch
 cherry-pick-bd7aa9779873.patch
 cherry-pick-2a434fd0af6b.patch
 cherry-pick-867c1001637e.patch
+cherry-pick-0b785e88fefa.patch
+cherry-pick-511cfef8e050.patch

+ 523 - 0
patches/DirectXShaderCompiler/cherry-pick-0b785e88fefa.patch

@@ -0,0 +1,523 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Antonio Maiorano <[email protected]>
+Date: Fri, 24 May 2024 15:51:26 -0400
+Subject: Fix dxil-remove-dead-blocks removing switch with multiple same
+ successor (#6610)
+
+Given a switch with a constant condition and all cases the same
+(branching to the same successor), dxil-remove-dead-blocks would
+incorrectly remove the switch when replacing it with a branch, by
+forgetting to remove the N-1 incoming values to the PHIs in the
+successor block.
+
+Bug: chromium:338071106
+Change-Id: Iaa2c42642f3e370afd19d88c96c81056c16349b6
+Reviewed-on: https://chromium-review.googlesource.com/c/external/github.com/microsoft/DirectXShaderCompiler/+/5570270
+Reviewed-by: Ben Clayton <[email protected]>
+Reviewed-by: James Price <[email protected]>
+
+diff --git a/lib/Transforms/Scalar/DxilRemoveDeadBlocks.cpp b/lib/Transforms/Scalar/DxilRemoveDeadBlocks.cpp
+index 54308eed2e018903e518be4a5ff809e080be78c0..9a87f4e6740c8da0522c6bf00f2a365f838cb3c0 100644
+--- a/lib/Transforms/Scalar/DxilRemoveDeadBlocks.cpp
++++ b/lib/Transforms/Scalar/DxilRemoveDeadBlocks.cpp
+@@ -35,6 +35,7 @@
+ using namespace llvm;
+ using namespace hlsl;
+ 
++// Removes BB from PHI nodes in SuccBB, deleting the PHI nodes if empty.
+ static void RemoveIncomingValueFrom(BasicBlock *SuccBB, BasicBlock *BB) {
+   for (auto inst_it = SuccBB->begin(); inst_it != SuccBB->end();) {
+     Instruction *I = &*(inst_it++);
+@@ -105,6 +106,8 @@ bool DeadBlockDeleter::Run(Function &F, DxilValueCache *DVC) {
+     } else if (SwitchInst *Switch = dyn_cast<SwitchInst>(BB->getTerminator())) {
+       Value *Cond = Switch->getCondition();
+       BasicBlock *Succ = nullptr;
++      // If the condition to Switch is constant, replace Switch with a branch
++      // to the current case successor.
+       if (ConstantInt *ConstCond = DVC->GetConstInt(Cond)) {
+         Succ = hlsl::dxilutil::GetSwitchSuccessorForCond(Switch, ConstCond);
+       }
+@@ -112,16 +115,32 @@ bool DeadBlockDeleter::Run(Function &F, DxilValueCache *DVC) {
+       if (Succ) {
+         Add(Succ);
+ 
++        // Create branch from BB to Succ that will replace Switch.
++        // This adds BB to preds of Succ.
+         BranchInst *NewBr = BranchInst::Create(Succ, BB);
+         hlsl::DxilMDHelper::CopyMetadata(*NewBr, *Switch);
+ 
++        // For any successors we're not going to, remove incoming block BB from
++        // PHI nodes in those successors.
++        unsigned numSucc = 0;
+         for (unsigned i = 0; i < Switch->getNumSuccessors(); i++) {
+           BasicBlock *NotSucc = Switch->getSuccessor(i);
+-          if (NotSucc != Succ) {
++          if (NotSucc != Succ)
+             RemoveIncomingValueFrom(NotSucc, BB);
+-          }
++          else
++            ++numSucc;
++        }
++
++        // We're replacing Switch with a single unconditional branch. If Switch
++        // has N cases with the same Succ, we need to remove N-1 incoming values
++        // of BB from the PHI nodes in Succ. This ensures that the preds of Succ
++        // match the ones in its PHIs.
++        for (unsigned i = 1; i < numSucc; i++) {
++          RemoveIncomingValueFrom(Succ, BB);
+         }
+ 
++        // Finally, erase Switch, which will remove BB as pred from all
++        // successors.
+         Switch->eraseFromParent();
+         Switch = nullptr;
+         Changed = true;
+diff --git a/tools/clang/test/DXC/Passes/DxilRemoveDeadBlocks/switch-with-multiple-same-successor.hlsl b/tools/clang/test/DXC/Passes/DxilRemoveDeadBlocks/switch-with-multiple-same-successor.hlsl
+new file mode 100644
+index 0000000000000000000000000000000000000000..43c3510b2ce18b15ff74a0db4697898da807f49f
+--- /dev/null
++++ b/tools/clang/test/DXC/Passes/DxilRemoveDeadBlocks/switch-with-multiple-same-successor.hlsl
+@@ -0,0 +1,68 @@
++// Test switch with multiple same successors
++// RUN: %dxc -T ps_6_6 %s | FileCheck %s
++
++// This test used to fail with validation errors:
++//
++// error: validation errors
++// error: Module bitcode is invalid.
++// error: PHINode should have one entry for each predecessor of its parent basic block!
++//   %22 = phi i32 [ 1, %20 ], [ 1, %20 ], [ 1, %20 ], [ 1, %20 ], [ %11, %13 ]
++// PHINode should have one entry for each predecessor of its parent basic block!
++//   %28 = phi i32 [ 1, %26 ], [ 1, %26 ], [ 1, %26 ], [ 1, %26 ], [ %22, %24 ]
++// PHINode should have one entry for each predecessor of its parent basic block!
++//   %34 = phi i32 [ 1, %32 ], [ 1, %32 ], [ 1, %32 ], [ 1, %32 ], [ %28, %30 ]
++// PHINode should have one entry for each predecessor of its parent basic block!
++//   %47 = phi i32 [ 1, %45 ], [ 1, %45 ], [ 1, %45 ], [ 1, %45 ], [ %41, %43 ]
++//
++// This was fixed in dxil-remove-dead-blocks. See switch-with-multiple-same-successor.ll
++// for the pass-specific checks. Here, we just want to make sure dxc compiles this without error.
++
++// CHECK: @main
++
++ByteAddressBuffer g_buff : register(t0);
++
++struct retval {
++  float4 value : SV_Target0;
++};
++
++retval main() {
++  float4 g = asfloat(g_buff.Load4(0u));
++  bool do_discard = false;
++
++  for (int i = 0; i < 10; ++i) {
++    if (g.x != 0.0f)
++      continue;
++
++    // Switch with the same successor in all cases
++    switch(i) {
++      case 1: {
++        g.x = g.x;
++        break;
++      }
++      case 2: {
++        g.x = g.x;
++        break;
++      }
++      case 3: {
++        g.x = g.x;
++        break;
++      }
++      // Skip 'case 4' to avoid case range combining
++      case 5: {
++        g.x = g.x;
++        break;
++      }
++    }
++    if (i == 6) {
++      break;
++    }
++    g.x = atan2(1.0f, g.x);
++    do_discard = true;
++  }
++
++  if (do_discard) {
++    discard;
++  }
++
++  return (retval)0;
++}
+diff --git a/tools/clang/test/DXC/Passes/DxilRemoveDeadBlocks/switch-with-multiple-same-successor.ll b/tools/clang/test/DXC/Passes/DxilRemoveDeadBlocks/switch-with-multiple-same-successor.ll
+new file mode 100644
+index 0000000000000000000000000000000000000000..d3e7e2f1e40c816c4ed28bfc45a1569e130f472c
+--- /dev/null
++++ b/tools/clang/test/DXC/Passes/DxilRemoveDeadBlocks/switch-with-multiple-same-successor.ll
+@@ -0,0 +1,369 @@
++; RUN: %dxopt %s -hlsl-passes-resume -dxil-remove-dead-blocks -S | FileCheck %s
++
++; Validate that a switch with a constant condition and multiple of the same successor
++; is correctly removed, ensuring that PHIs in the successor are properly updated.
++; For instance, in:
++;
++;
++; if.end.1:                                         ; preds = %for.inc
++;   switch i32 1, label %sw.epilog.1 [
++;     i32 1, label %dx.struct_exit.new_exiting.1
++;     i32 2, label %dx.struct_exit.new_exiting.1
++;     i32 3, label %dx.struct_exit.new_exiting.1
++;     i32 5, label %dx.struct_exit.new_exiting.1
++;   ], !dbg !31 ; line:23 col:5
++;
++; sw.epilog.1:                                      ; preds = %if.end.1
++;   br label %dx.struct_exit.new_exiting.1
++;
++; dx.struct_exit.new_exiting.1:                     ; preds = %sw.epilog.1, %if.end.1, %if.end.1, %if.end.1, %if.end.1, %for.inc
++;   %dx.struct_exit.prop.1 = phi i32 [ %do_discard.2, %sw.epilog.1 ], [ 0, %if.end.1 ], [ 0, %if.end.1 ], [ 0, %if.end.1 ], [ 0, %if.end.1 ], [ 0, %for.inc ]
++;   %do_discard.2.1 = phi i32 [ 1, %sw.epilog.1 ], [ 1, %if.end.1 ], [ 1, %if.end.1 ], [ 1, %if.end.1 ], [ 1, %if.end.1 ], [ %do_discard.2, %for.inc ]
++;   %g.2.i0.1 = phi float [ 0x3FF921FB60000000, %sw.epilog.1 ], [ 0x3FF921FB60000000, %if.end.1 ], [ 0x3FF921FB60000000, %if.end.1 ], [ 0x3FF921FB60000000, %if.end.1 ], [ 0x3FF921FB60000000, %if.end.1 ], [ %g.2.i0, %for.inc ]
++;   br i1 false, label %cleanup, label %for.inc.1
++;
++;
++; After dxil-remove-dead-blocks, the multiple `%if.end.1` in preds and in the two phi instructions should be removed,
++; and only one instance should be left.
++
++; CHECK:      if.end.1:                                         ; preds = %for.inc
++; CHECK-NEXT:   br label %dx.struct_exit.new_exiting.1
++
++; CHECK:     dx.struct_exit.new_exiting.1:                     ; preds = %if.end.1, %for.inc
++; CHECK-NEXT:  %do_discard.2.1 = phi i32 [ 1, %if.end.1 ], [ %do_discard.2, %for.inc ]
++; CHECK-NEXT:  %g.2.i0.1 = phi float [ 0x3FF921FB60000000, %if.end.1 ], [ %g.2.i0, %for.inc ]
++
++;
++; Output signature:
++;
++; Name                 Index             InterpMode DynIdx
++; -------------------- ----- ---------------------- ------
++; SV_Target                0
++;
++; Buffer Definitions:
++;
++;
++; Resource Bindings:
++;
++; Name                                 Type  Format         Dim      ID      HLSL Bind  Count
++; ------------------------------ ---------- ------- ----------- ------- -------------- ------
++; g_buff                            texture    byte         r/o      T0             t0     1
++;
++target datalayout = "e-m:e-p:32:32-i1:32-i8:32-i16:32-i32:32-i64:64-f16:32-f32:32-f64:64-n8:16:32:64"
++target triple = "dxil-ms-dx"
++
++%struct.ByteAddressBuffer = type { i32 }
++%dx.types.Handle = type { i8* }
++%dx.types.ResourceProperties = type { i32, i32 }
++%dx.types.ResRet.i32 = type { i32, i32, i32, i32, i32 }
++%struct.retval = type { <4 x float> }
++
++@"\01?g_buff@@3UByteAddressBuffer@@A" = external global %struct.ByteAddressBuffer, align 4
[email protected] = appending global [1 x i8*] [i8* bitcast (%struct.ByteAddressBuffer* @"\01?g_buff@@3UByteAddressBuffer@@A" to i8*)], section "llvm.metadata"
++
++; Function Attrs: nounwind
++define void @main(<4 x float>* noalias nocapture readnone) #0 {
++for.body.lr.ph:
++  %1 = load %struct.ByteAddressBuffer, %struct.ByteAddressBuffer* @"\01?g_buff@@3UByteAddressBuffer@@A", align 4, !dbg !23 ; line:15 col:22
++  %2 = call %dx.types.Handle @dx.op.createHandleForLib.struct.ByteAddressBuffer(i32 160, %struct.ByteAddressBuffer %1), !dbg !23 ; line:15 col:22  ; CreateHandleForLib(Resource)
++  %3 = call %dx.types.Handle @dx.op.annotateHandle(i32 216, %dx.types.Handle %2, %dx.types.ResourceProperties { i32 11, i32 0 }), !dbg !23 ; line:15 col:22  ; AnnotateHandle(res,props)  resource: ByteAddressBuffer
++  %RawBufferLoad = call %dx.types.ResRet.i32 @dx.op.rawBufferLoad.i32(i32 139, %dx.types.Handle %3, i32 0, i32 undef, i8 15, i32 4), !dbg !23 ; line:15 col:22  ; RawBufferLoad(srv,index,elementOffset,mask,alignment)
++  %4 = extractvalue %dx.types.ResRet.i32 %RawBufferLoad, 0, !dbg !23 ; line:15 col:22
++  %.i0 = bitcast i32 %4 to float, !dbg !27 ; line:15 col:14
++  br label %for.body, !dbg !28 ; line:18 col:3
++
++for.body:                                         ; preds = %for.body.lr.ph
++  %cmp3 = fcmp fast une float %.i0, 0.000000e+00, !dbg !29 ; line:19 col:13
++  br i1 %cmp3, label %dx.struct_exit.new_exiting, label %if.end, !dbg !30 ; line:19 col:9
++
++if.end:                                           ; preds = %for.body
++  switch i32 0, label %sw.epilog [
++    i32 1, label %dx.struct_exit.new_exiting
++    i32 2, label %dx.struct_exit.new_exiting
++    i32 3, label %dx.struct_exit.new_exiting
++    i32 5, label %dx.struct_exit.new_exiting
++  ], !dbg !31 ; line:23 col:5
++
++sw.epilog:                                        ; preds = %if.end
++  br label %dx.struct_exit.new_exiting
++
++dx.struct_exit.new_exiting:                       ; preds = %sw.epilog, %if.end, %if.end, %if.end, %if.end, %for.body
++  %do_discard.2 = phi i32 [ 0, %for.body ], [ 1, %if.end ], [ 1, %if.end ], [ 1, %if.end ], [ 1, %if.end ], [ 1, %sw.epilog ]
++  %g.2.i0 = phi float [ %.i0, %for.body ], [ 0x3FF921FB60000000, %if.end ], [ 0x3FF921FB60000000, %if.end ], [ 0x3FF921FB60000000, %if.end ], [ 0x3FF921FB60000000, %if.end ], [ 0x3FF921FB60000000, %sw.epilog ]
++  br i1 false, label %cleanup, label %for.inc
++
++for.inc:                                          ; preds = %dx.struct_exit.new_exiting
++  %cmp3.1 = fcmp fast une float %g.2.i0, 0.000000e+00, !dbg !29 ; line:19 col:13
++  br i1 %cmp3.1, label %dx.struct_exit.new_exiting.1, label %if.end.1, !dbg !30 ; line:19 col:9
++
++cleanup:                                          ; preds = %for.inc.9, %dx.struct_exit.new_exiting.9, %dx.struct_exit.new_exiting.8, %dx.struct_exit.new_exiting.7, %dx.struct_exit.new_exiting.6, %dx.struct_exit.new_exiting.5, %dx.struct_exit.new_exiting.4, %dx.struct_exit.new_exiting.3, %dx.struct_exit.new_exiting.2, %dx.struct_exit.new_exiting.1, %dx.struct_exit.new_exiting
++  %do_discard.3 = phi i32 [ 0, %dx.struct_exit.new_exiting ], [ %dx.struct_exit.prop.1, %dx.struct_exit.new_exiting.1 ], [ %dx.struct_exit.prop.2, %dx.struct_exit.new_exiting.2 ], [ %dx.struct_exit.prop.3, %dx.struct_exit.new_exiting.3 ], [ %dx.struct_exit.prop.4, %dx.struct_exit.new_exiting.4 ], [ %dx.struct_exit.prop.5, %dx.struct_exit.new_exiting.5 ], [ %dx.struct_exit.prop.6, %dx.struct_exit.new_exiting.6 ], [ %dx.struct_exit.prop.7, %dx.struct_exit.new_exiting.7 ], [ %dx.struct_exit.prop.8, %dx.struct_exit.new_exiting.8 ], [ %dx.struct_exit.prop.9, %dx.struct_exit.new_exiting.9 ], [ %do_discard.2.9, %for.inc.9 ]
++  %tobool15 = icmp eq i32 %do_discard.3, 0, !dbg !32 ; line:49 col:7
++  br i1 %tobool15, label %if.end.17, label %if.then.16, !dbg !32 ; line:49 col:7
++
++if.then.16:                                       ; preds = %cleanup
++  call void @dx.op.discard(i32 82, i1 true), !dbg !33 ; line:49 col:19  ; Discard(condition)
++  br label %if.end.17, !dbg !34 ; line:51 col:3
++
++if.end.17:                                        ; preds = %cleanup, %if.then.16
++  call void @dx.op.storeOutput.f32(i32 5, i32 0, i32 0, i8 0, float 0.000000e+00), !dbg !35 ; line:53 col:18  ; StoreOutput(outputSigId,rowIndex,colIndex,value)
++  call void @dx.op.storeOutput.f32(i32 5, i32 0, i32 0, i8 1, float 0.000000e+00), !dbg !35 ; line:53 col:18  ; StoreOutput(outputSigId,rowIndex,colIndex,value)
++  call void @dx.op.storeOutput.f32(i32 5, i32 0, i32 0, i8 2, float 0.000000e+00), !dbg !35 ; line:53 col:18  ; StoreOutput(outputSigId,rowIndex,colIndex,value)
++  call void @dx.op.storeOutput.f32(i32 5, i32 0, i32 0, i8 3, float 0.000000e+00), !dbg !35 ; line:53 col:18  ; StoreOutput(outputSigId,rowIndex,colIndex,value)
++  ret void, !dbg !36 ; line:54 col:1
++
++if.end.1:                                         ; preds = %for.inc
++  switch i32 1, label %sw.epilog.1 [
++    i32 1, label %dx.struct_exit.new_exiting.1
++    i32 2, label %dx.struct_exit.new_exiting.1
++    i32 3, label %dx.struct_exit.new_exiting.1
++    i32 5, label %dx.struct_exit.new_exiting.1
++  ], !dbg !31 ; line:23 col:5
++
++sw.epilog.1:                                      ; preds = %if.end.1
++  br label %dx.struct_exit.new_exiting.1
++
++dx.struct_exit.new_exiting.1:                     ; preds = %sw.epilog.1, %if.end.1, %if.end.1, %if.end.1, %if.end.1, %for.inc
++  %dx.struct_exit.prop.1 = phi i32 [ %do_discard.2, %sw.epilog.1 ], [ 0, %if.end.1 ], [ 0, %if.end.1 ], [ 0, %if.end.1 ], [ 0, %if.end.1 ], [ 0, %for.inc ]
++  %do_discard.2.1 = phi i32 [ 1, %sw.epilog.1 ], [ 1, %if.end.1 ], [ 1, %if.end.1 ], [ 1, %if.end.1 ], [ 1, %if.end.1 ], [ %do_discard.2, %for.inc ]
++  %g.2.i0.1 = phi float [ 0x3FF921FB60000000, %sw.epilog.1 ], [ 0x3FF921FB60000000, %if.end.1 ], [ 0x3FF921FB60000000, %if.end.1 ], [ 0x3FF921FB60000000, %if.end.1 ], [ 0x3FF921FB60000000, %if.end.1 ], [ %g.2.i0, %for.inc ]
++  br i1 false, label %cleanup, label %for.inc.1
++
++for.inc.1:                                        ; preds = %dx.struct_exit.new_exiting.1
++  %cmp3.2 = fcmp fast une float %g.2.i0.1, 0.000000e+00, !dbg !29 ; line:19 col:13
++  br i1 %cmp3.2, label %dx.struct_exit.new_exiting.2, label %if.end.2, !dbg !30 ; line:19 col:9
++
++if.end.2:                                         ; preds = %for.inc.1
++  switch i32 2, label %sw.epilog.2 [
++    i32 1, label %dx.struct_exit.new_exiting.2
++    i32 2, label %dx.struct_exit.new_exiting.2
++    i32 3, label %dx.struct_exit.new_exiting.2
++    i32 5, label %dx.struct_exit.new_exiting.2
++  ], !dbg !31 ; line:23 col:5
++
++sw.epilog.2:                                      ; preds = %if.end.2
++  br label %dx.struct_exit.new_exiting.2
++
++dx.struct_exit.new_exiting.2:                     ; preds = %sw.epilog.2, %if.end.2, %if.end.2, %if.end.2, %if.end.2, %for.inc.1
++  %dx.struct_exit.prop.2 = phi i32 [ %do_discard.2.1, %sw.epilog.2 ], [ 0, %if.end.2 ], [ 0, %if.end.2 ], [ 0, %if.end.2 ], [ 0, %if.end.2 ], [ 0, %for.inc.1 ]
++  %do_discard.2.2 = phi i32 [ 1, %sw.epilog.2 ], [ 1, %if.end.2 ], [ 1, %if.end.2 ], [ 1, %if.end.2 ], [ 1, %if.end.2 ], [ %do_discard.2.1, %for.inc.1 ]
++  %g.2.i0.2 = phi float [ 0x3FF921FB60000000, %sw.epilog.2 ], [ 0x3FF921FB60000000, %if.end.2 ], [ 0x3FF921FB60000000, %if.end.2 ], [ 0x3FF921FB60000000, %if.end.2 ], [ 0x3FF921FB60000000, %if.end.2 ], [ %g.2.i0.1, %for.inc.1 ]
++  br i1 false, label %cleanup, label %for.inc.2
++
++for.inc.2:                                        ; preds = %dx.struct_exit.new_exiting.2
++  %cmp3.3 = fcmp fast une float %g.2.i0.2, 0.000000e+00, !dbg !29 ; line:19 col:13
++  br i1 %cmp3.3, label %dx.struct_exit.new_exiting.3, label %if.end.3, !dbg !30 ; line:19 col:9
++
++if.end.3:                                         ; preds = %for.inc.2
++  switch i32 3, label %sw.epilog.3 [
++    i32 1, label %dx.struct_exit.new_exiting.3
++    i32 2, label %dx.struct_exit.new_exiting.3
++    i32 3, label %dx.struct_exit.new_exiting.3
++    i32 5, label %dx.struct_exit.new_exiting.3
++  ], !dbg !31 ; line:23 col:5
++
++sw.epilog.3:                                      ; preds = %if.end.3
++  br label %dx.struct_exit.new_exiting.3
++
++dx.struct_exit.new_exiting.3:                     ; preds = %sw.epilog.3, %if.end.3, %if.end.3, %if.end.3, %if.end.3, %for.inc.2
++  %dx.struct_exit.prop.3 = phi i32 [ %do_discard.2.2, %sw.epilog.3 ], [ 0, %if.end.3 ], [ 0, %if.end.3 ], [ 0, %if.end.3 ], [ 0, %if.end.3 ], [ 0, %for.inc.2 ]
++  %do_discard.2.3 = phi i32 [ 1, %sw.epilog.3 ], [ 1, %if.end.3 ], [ 1, %if.end.3 ], [ 1, %if.end.3 ], [ 1, %if.end.3 ], [ %do_discard.2.2, %for.inc.2 ]
++  %g.2.i0.3 = phi float [ 0x3FF921FB60000000, %sw.epilog.3 ], [ 0x3FF921FB60000000, %if.end.3 ], [ 0x3FF921FB60000000, %if.end.3 ], [ 0x3FF921FB60000000, %if.end.3 ], [ 0x3FF921FB60000000, %if.end.3 ], [ %g.2.i0.2, %for.inc.2 ]
++  br i1 false, label %cleanup, label %for.inc.3
++
++for.inc.3:                                        ; preds = %dx.struct_exit.new_exiting.3
++  %cmp3.4 = fcmp fast une float %g.2.i0.3, 0.000000e+00, !dbg !29 ; line:19 col:13
++  br i1 %cmp3.4, label %dx.struct_exit.new_exiting.4, label %if.end.4, !dbg !30 ; line:19 col:9
++
++if.end.4:                                         ; preds = %for.inc.3
++  switch i32 4, label %sw.epilog.4 [
++    i32 1, label %dx.struct_exit.new_exiting.4
++    i32 2, label %dx.struct_exit.new_exiting.4
++    i32 3, label %dx.struct_exit.new_exiting.4
++    i32 5, label %dx.struct_exit.new_exiting.4
++  ], !dbg !31 ; line:23 col:5
++
++sw.epilog.4:                                      ; preds = %if.end.4
++  br label %dx.struct_exit.new_exiting.4
++
++dx.struct_exit.new_exiting.4:                     ; preds = %sw.epilog.4, %if.end.4, %if.end.4, %if.end.4, %if.end.4, %for.inc.3
++  %dx.struct_exit.prop.4 = phi i32 [ %do_discard.2.3, %sw.epilog.4 ], [ 0, %if.end.4 ], [ 0, %if.end.4 ], [ 0, %if.end.4 ], [ 0, %if.end.4 ], [ 0, %for.inc.3 ]
++  %do_discard.2.4 = phi i32 [ 1, %sw.epilog.4 ], [ 1, %if.end.4 ], [ 1, %if.end.4 ], [ 1, %if.end.4 ], [ 1, %if.end.4 ], [ %do_discard.2.3, %for.inc.3 ]
++  %g.2.i0.4 = phi float [ 0x3FF921FB60000000, %sw.epilog.4 ], [ 0x3FF921FB60000000, %if.end.4 ], [ 0x3FF921FB60000000, %if.end.4 ], [ 0x3FF921FB60000000, %if.end.4 ], [ 0x3FF921FB60000000, %if.end.4 ], [ %g.2.i0.3, %for.inc.3 ]
++  br i1 false, label %cleanup, label %for.inc.4
++
++for.inc.4:                                        ; preds = %dx.struct_exit.new_exiting.4
++  %cmp3.5 = fcmp fast une float %g.2.i0.4, 0.000000e+00, !dbg !29 ; line:19 col:13
++  br i1 %cmp3.5, label %dx.struct_exit.new_exiting.5, label %if.end.5, !dbg !30 ; line:19 col:9
++
++if.end.5:                                         ; preds = %for.inc.4
++  switch i32 5, label %sw.epilog.5 [
++    i32 1, label %dx.struct_exit.new_exiting.5
++    i32 2, label %dx.struct_exit.new_exiting.5
++    i32 3, label %dx.struct_exit.new_exiting.5
++    i32 5, label %dx.struct_exit.new_exiting.5
++  ], !dbg !31 ; line:23 col:5
++
++sw.epilog.5:                                      ; preds = %if.end.5
++  br label %dx.struct_exit.new_exiting.5
++
++dx.struct_exit.new_exiting.5:                     ; preds = %sw.epilog.5, %if.end.5, %if.end.5, %if.end.5, %if.end.5, %for.inc.4
++  %dx.struct_exit.prop.5 = phi i32 [ %do_discard.2.4, %sw.epilog.5 ], [ 0, %if.end.5 ], [ 0, %if.end.5 ], [ 0, %if.end.5 ], [ 0, %if.end.5 ], [ 0, %for.inc.4 ]
++  %do_discard.2.5 = phi i32 [ 1, %sw.epilog.5 ], [ 1, %if.end.5 ], [ 1, %if.end.5 ], [ 1, %if.end.5 ], [ 1, %if.end.5 ], [ %do_discard.2.4, %for.inc.4 ]
++  %g.2.i0.5 = phi float [ 0x3FF921FB60000000, %sw.epilog.5 ], [ 0x3FF921FB60000000, %if.end.5 ], [ 0x3FF921FB60000000, %if.end.5 ], [ 0x3FF921FB60000000, %if.end.5 ], [ 0x3FF921FB60000000, %if.end.5 ], [ %g.2.i0.4, %for.inc.4 ]
++  br i1 false, label %cleanup, label %for.inc.5
++
++for.inc.5:                                        ; preds = %dx.struct_exit.new_exiting.5
++  %cmp3.6 = fcmp fast une float %g.2.i0.5, 0.000000e+00, !dbg !29 ; line:19 col:13
++  br i1 %cmp3.6, label %dx.struct_exit.new_exiting.6, label %if.end.6, !dbg !30 ; line:19 col:9
++
++if.end.6:                                         ; preds = %for.inc.5
++  switch i32 6, label %sw.epilog.6 [
++    i32 1, label %dx.struct_exit.new_exiting.6
++    i32 2, label %dx.struct_exit.new_exiting.6
++    i32 3, label %dx.struct_exit.new_exiting.6
++    i32 5, label %dx.struct_exit.new_exiting.6
++  ], !dbg !31 ; line:23 col:5
++
++sw.epilog.6:                                      ; preds = %if.end.6
++  br label %dx.struct_exit.new_exiting.6
++
++dx.struct_exit.new_exiting.6:                     ; preds = %sw.epilog.6, %if.end.6, %if.end.6, %if.end.6, %if.end.6, %for.inc.5
++  %dx.struct_exit.prop23.6 = phi i1 [ true, %sw.epilog.6 ], [ false, %if.end.6 ], [ false, %if.end.6 ], [ false, %if.end.6 ], [ false, %if.end.6 ], [ false, %for.inc.5 ]
++  %dx.struct_exit.prop.6 = phi i32 [ %do_discard.2.5, %sw.epilog.6 ], [ 0, %if.end.6 ], [ 0, %if.end.6 ], [ 0, %if.end.6 ], [ 0, %if.end.6 ], [ 0, %for.inc.5 ]
++  %do_discard.2.6 = phi i32 [ 1, %sw.epilog.6 ], [ 1, %if.end.6 ], [ 1, %if.end.6 ], [ 1, %if.end.6 ], [ 1, %if.end.6 ], [ %do_discard.2.5, %for.inc.5 ]
++  %g.2.i0.6 = phi float [ 0x3FF921FB60000000, %sw.epilog.6 ], [ 0x3FF921FB60000000, %if.end.6 ], [ 0x3FF921FB60000000, %if.end.6 ], [ 0x3FF921FB60000000, %if.end.6 ], [ 0x3FF921FB60000000, %if.end.6 ], [ %g.2.i0.5, %for.inc.5 ]
++  br i1 %dx.struct_exit.prop23.6, label %cleanup, label %for.inc.6
++
++for.inc.6:                                        ; preds = %dx.struct_exit.new_exiting.6
++  %cmp3.7 = fcmp fast une float %g.2.i0.6, 0.000000e+00, !dbg !29 ; line:19 col:13
++  br i1 %cmp3.7, label %dx.struct_exit.new_exiting.7, label %if.end.7, !dbg !30 ; line:19 col:9
++
++if.end.7:                                         ; preds = %for.inc.6
++  switch i32 7, label %sw.epilog.7 [
++    i32 1, label %dx.struct_exit.new_exiting.7
++    i32 2, label %dx.struct_exit.new_exiting.7
++    i32 3, label %dx.struct_exit.new_exiting.7
++    i32 5, label %dx.struct_exit.new_exiting.7
++  ], !dbg !31 ; line:23 col:5
++
++sw.epilog.7:                                      ; preds = %if.end.7
++  br label %dx.struct_exit.new_exiting.7
++
++dx.struct_exit.new_exiting.7:                     ; preds = %sw.epilog.7, %if.end.7, %if.end.7, %if.end.7, %if.end.7, %for.inc.6
++  %dx.struct_exit.prop.7 = phi i32 [ %do_discard.2.6, %sw.epilog.7 ], [ 0, %if.end.7 ], [ 0, %if.end.7 ], [ 0, %if.end.7 ], [ 0, %if.end.7 ], [ 0, %for.inc.6 ]
++  %do_discard.2.7 = phi i32 [ 1, %sw.epilog.7 ], [ 1, %if.end.7 ], [ 1, %if.end.7 ], [ 1, %if.end.7 ], [ 1, %if.end.7 ], [ %do_discard.2.6, %for.inc.6 ]
++  %g.2.i0.7 = phi float [ 0x3FF921FB60000000, %sw.epilog.7 ], [ 0x3FF921FB60000000, %if.end.7 ], [ 0x3FF921FB60000000, %if.end.7 ], [ 0x3FF921FB60000000, %if.end.7 ], [ 0x3FF921FB60000000, %if.end.7 ], [ %g.2.i0.6, %for.inc.6 ]
++  br i1 false, label %cleanup, label %for.inc.7
++
++for.inc.7:                                        ; preds = %dx.struct_exit.new_exiting.7
++  %cmp3.8 = fcmp fast une float %g.2.i0.7, 0.000000e+00, !dbg !29 ; line:19 col:13
++  br i1 %cmp3.8, label %dx.struct_exit.new_exiting.8, label %if.end.8, !dbg !30 ; line:19 col:9
++
++if.end.8:                                         ; preds = %for.inc.7
++  switch i32 8, label %sw.epilog.8 [
++    i32 1, label %dx.struct_exit.new_exiting.8
++    i32 2, label %dx.struct_exit.new_exiting.8
++    i32 3, label %dx.struct_exit.new_exiting.8
++    i32 5, label %dx.struct_exit.new_exiting.8
++  ], !dbg !31 ; line:23 col:5
++
++sw.epilog.8:                                      ; preds = %if.end.8
++  br label %dx.struct_exit.new_exiting.8
++
++dx.struct_exit.new_exiting.8:                     ; preds = %sw.epilog.8, %if.end.8, %if.end.8, %if.end.8, %if.end.8, %for.inc.7
++  %dx.struct_exit.prop.8 = phi i32 [ %do_discard.2.7, %sw.epilog.8 ], [ 0, %if.end.8 ], [ 0, %if.end.8 ], [ 0, %if.end.8 ], [ 0, %if.end.8 ], [ 0, %for.inc.7 ]
++  %do_discard.2.8 = phi i32 [ 1, %sw.epilog.8 ], [ 1, %if.end.8 ], [ 1, %if.end.8 ], [ 1, %if.end.8 ], [ 1, %if.end.8 ], [ %do_discard.2.7, %for.inc.7 ]
++  %g.2.i0.8 = phi float [ 0x3FF921FB60000000, %sw.epilog.8 ], [ 0x3FF921FB60000000, %if.end.8 ], [ 0x3FF921FB60000000, %if.end.8 ], [ 0x3FF921FB60000000, %if.end.8 ], [ 0x3FF921FB60000000, %if.end.8 ], [ %g.2.i0.7, %for.inc.7 ]
++  br i1 false, label %cleanup, label %for.inc.8
++
++for.inc.8:                                        ; preds = %dx.struct_exit.new_exiting.8
++  %cmp3.9 = fcmp fast une float %g.2.i0.8, 0.000000e+00, !dbg !29 ; line:19 col:13
++  br i1 %cmp3.9, label %dx.struct_exit.new_exiting.9, label %if.end.9, !dbg !30 ; line:19 col:9
++
++if.end.9:                                         ; preds = %for.inc.8
++  switch i32 9, label %sw.epilog.9 [
++    i32 1, label %dx.struct_exit.new_exiting.9
++    i32 2, label %dx.struct_exit.new_exiting.9
++    i32 3, label %dx.struct_exit.new_exiting.9
++    i32 5, label %dx.struct_exit.new_exiting.9
++  ], !dbg !31 ; line:23 col:5
++
++sw.epilog.9:                                      ; preds = %if.end.9
++  br label %dx.struct_exit.new_exiting.9
++
++dx.struct_exit.new_exiting.9:                     ; preds = %sw.epilog.9, %if.end.9, %if.end.9, %if.end.9, %if.end.9, %for.inc.8
++  %dx.struct_exit.prop.9 = phi i32 [ %do_discard.2.8, %sw.epilog.9 ], [ 0, %if.end.9 ], [ 0, %if.end.9 ], [ 0, %if.end.9 ], [ 0, %if.end.9 ], [ 0, %for.inc.8 ]
++  %do_discard.2.9 = phi i32 [ 1, %sw.epilog.9 ], [ 1, %if.end.9 ], [ 1, %if.end.9 ], [ 1, %if.end.9 ], [ 1, %if.end.9 ], [ %do_discard.2.8, %for.inc.8 ]
++  br i1 false, label %cleanup, label %for.inc.9
++
++for.inc.9:                                        ; preds = %dx.struct_exit.new_exiting.9
++  br label %cleanup
++}
++
++; Function Attrs: nounwind
++declare void @dx.op.storeOutput.f32(i32, i32, i32, i8, float) #0
++
++; Function Attrs: nounwind
++declare void @dx.op.discard(i32, i1) #0
++
++; Function Attrs: nounwind readonly
++declare %dx.types.ResRet.i32 @dx.op.rawBufferLoad.i32(i32, %dx.types.Handle, i32, i32, i8, i32) #1
++
++; Function Attrs: nounwind readonly
++declare %dx.types.Handle @dx.op.createHandleForLib.struct.ByteAddressBuffer(i32, %struct.ByteAddressBuffer) #1
++
++; Function Attrs: nounwind readnone
++declare %dx.types.Handle @dx.op.annotateHandle(i32, %dx.types.Handle, %dx.types.ResourceProperties) #2
++
++attributes #0 = { nounwind }
++attributes #1 = { nounwind readonly }
++attributes #2 = { nounwind readnone }
++
++!llvm.module.flags = !{!0}
++!pauseresume = !{!1}
++!llvm.ident = !{!2}
++!dx.version = !{!3}
++!dx.valver = !{!4}
++!dx.shaderModel = !{!5}
++!dx.resources = !{!6}
++!dx.typeAnnotations = !{!9, !12}
++!dx.entryPoints = !{!19}
++
++!0 = !{i32 2, !"Debug Info Version", i32 3}
++!1 = !{!"hlsl-dxilemit", !"hlsl-dxilload"}
++!2 = !{!"dxc(private) 1.8.0.4514 (d9bd2a706-dirty)"}
++!3 = !{i32 1, i32 6}
++!4 = !{i32 1, i32 8}
++!5 = !{!"ps", i32 6, i32 6}
++!6 = !{!7, null, null, null}
++!7 = !{!8}
++!8 = !{i32 0, %struct.ByteAddressBuffer* @"\01?g_buff@@3UByteAddressBuffer@@A", !"g_buff", i32 0, i32 0, i32 1, i32 11, i32 0, null}
++!9 = !{i32 0, %struct.retval undef, !10}
++!10 = !{i32 16, !11}
++!11 = !{i32 6, !"value", i32 3, i32 0, i32 4, !"SV_Target0", i32 7, i32 9}
++!12 = !{i32 1, void (<4 x float>*)* @main, !13}
++!13 = !{!14, !16}
++!14 = !{i32 0, !15, !15}
++!15 = !{}
++!16 = !{i32 1, !17, !18}
++!17 = !{i32 4, !"SV_Target0", i32 7, i32 9}
++!18 = !{i32 0}
++!19 = !{void (<4 x float>*)* @main, !"main", !20, !6, null}
++!20 = !{null, !21, null}
++!21 = !{!22}
++!22 = !{i32 0, !"SV_Target", i8 9, i8 16, !18, i8 0, i32 1, i8 4, i32 0, i8 0, null}
++!23 = !DILocation(line: 15, column: 22, scope: !24)
++!24 = !DISubprogram(name: "main", scope: !25, file: !25, line: 14, type: !26, isLocal: false, isDefinition: true, scopeLine: 14, flags: DIFlagPrototyped, isOptimized: false, function: void (<4 x float>*)* @main)
++!25 = !DIFile(filename: "/home/amaiorano/src/external/DirectXShaderCompiler/tools/clang/test/DXC/Passes/DxilRemoveDeadBlocks/switch-with-multiple-same-successor.hlsl", directory: "")
++!26 = !DISubroutineType(types: !15)
++!27 = !DILocation(line: 15, column: 14, scope: !24)
++!28 = !DILocation(line: 18, column: 3, scope: !24)
++!29 = !DILocation(line: 19, column: 13, scope: !24)
++!30 = !DILocation(line: 19, column: 9, scope: !24)
++!31 = !DILocation(line: 23, column: 5, scope: !24)
++!32 = !DILocation(line: 49, column: 7, scope: !24)
++!33 = !DILocation(line: 49, column: 19, scope: !24)
++!34 = !DILocation(line: 51, column: 3, scope: !24)
++!35 = !DILocation(line: 53, column: 18, scope: !24)
++!36 = !DILocation(line: 54, column: 1, scope: !24)

+ 243 - 0
patches/DirectXShaderCompiler/cherry-pick-511cfef8e050.patch

@@ -0,0 +1,243 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Antonio Maiorano <[email protected]>
+Date: Mon, 27 May 2024 15:41:40 -0400
+Subject: Fix use-after-free in SimplifyCFG (#6628)
+
+When SimplifySwitchOnSelect calls SimplifyTerminatorOnSelect, it holds
+onto the select's condition value to use for the conditional branch it
+replaces the switch with. When removing the switch's unused
+predecessors, it must make sure not to delete PHIs in case one of them
+is used by the condition value, otherwise the condition value itself may
+get deleted, resulting in an use-after-free.
+
+Note that this was fixed in LLVM as well:
+
+https://github.com/llvm/llvm-project/commit/dc3b67b4cad5c18a687edfabd50779c3c656c620
+
+Bug: chromium:338103465
+Change-Id: Iff5d5f2e3ecf38a3fb22bbc65e7c33ad0de659fb
+Reviewed-on: https://chromium-review.googlesource.com/c/external/github.com/microsoft/DirectXShaderCompiler/+/5570018
+Reviewed-by: James Price <[email protected]>
+Reviewed-by: Natalie Chouinard <[email protected]>
+
+diff --git a/lib/Transforms/Utils/SimplifyCFG.cpp b/lib/Transforms/Utils/SimplifyCFG.cpp
+index b45caa2929a5cc3aa064fdbd9c06c20ad9e1e155..0d3ba1e00719060c1e71fa238726f0c63bd5b32f 100644
+--- a/lib/Transforms/Utils/SimplifyCFG.cpp
++++ b/lib/Transforms/Utils/SimplifyCFG.cpp
+@@ -2619,7 +2619,10 @@ static bool SimplifyTerminatorOnSelect(TerminatorInst *OldTerm, Value *Cond,
+     else if (Succ == KeepEdge2)
+       KeepEdge2 = nullptr;
+     else
+-      Succ->removePredecessor(OldTerm->getParent());
++      Succ->removePredecessor(
++          OldTerm->getParent(),
++          /*DontDeleteUselessPHIs=*/true // HLSL Change: foward port LLVM fix
++      );
+   }
+ 
+   IRBuilder<> Builder(OldTerm);
+diff --git a/tools/clang/test/DXC/Passes/SimplifyCFG/simplifycfg-uaf-select-condition.ll b/tools/clang/test/DXC/Passes/SimplifyCFG/simplifycfg-uaf-select-condition.ll
+new file mode 100644
+index 0000000000000000000000000000000000000000..149906c11285ed99a19c0fe1743801a795827792
+--- /dev/null
++++ b/tools/clang/test/DXC/Passes/SimplifyCFG/simplifycfg-uaf-select-condition.ll
+@@ -0,0 +1,199 @@
++; RUN: %dxopt %s -hlsl-passes-resume -simplifycfg -S | FileCheck %s
++
++; The IR below comes from the following HLSL.
++; Compiling this HLSL with dxc was resulting in an ASAN
++; use-after-free in SimplifyCFG during
++; SimplifyTerminatorOnSelect because it was deleting
++; a PHI node with an input value that the pass later
++; emits (the select condition value).
++
++; ByteAddressBuffer buff : register(t0);
++;
++; [numthreads(1, 1, 1)]
++; void main() {
++;   if (buff.Load(0u)) {
++;     return;
++;   }
++;
++;   int i = 0;
++;   int j = 0;
++;   while (true) {
++;     bool a = (i < 2);
++;     switch(i) {
++;       case 0: {
++;         while (true) {
++;           bool b = (j < 2);
++;           if (b) {
++;           } else {
++;             break;
++;           }
++;           while (true) {
++;             int unused = 0;
++;             while (true) {
++;               if (a) break;
++;             }
++;             while (true) {
++;               while (true) {
++;                 if (b) {
++;                   if (b) return;
++;                 } else {
++;                   break;
++;                 }
++;                 while (true) {
++;                   i = 0;
++;                   if (b) break;
++;                 }
++;                 if (a) break;
++;               }
++;               if (a) break;
++;             }
++;             if (a) break;
++;           }
++;           j = (j + 2);
++;         }
++;       }
++;     }
++;   }
++; }
++
++; Make sure the phi node did not get deleted by simplifycfg
++; CHECK:       cleanup:
++; CHECK-NEXT:    %cleanup.dest.slot.0 = phi i32 [ 1, %while.body.20 ], [ %.mux, %while.end.37 ]
++; CHECK-NEXT:    switch i32 %cleanup.dest.slot.0, label %cleanup.46 [
++
++;
++; Buffer Definitions:
++;
++;
++; Resource Bindings:
++;
++; Name                                 Type  Format         Dim      ID      HLSL Bind  Count
++; ------------------------------ ---------- ------- ----------- ------- -------------- ------
++; buff                              texture    byte         r/o      T0             t0     1
++;
++target datalayout = "e-m:e-p:32:32-i1:32-i8:32-i16:32-i32:32-i64:64-f16:32-f32:32-f64:64-n8:16:32:64"
++target triple = "dxil-ms-dx"
++
++%struct.ByteAddressBuffer = type { i32 }
++%dx.types.Handle = type { i8* }
++%dx.types.ResourceProperties = type { i32, i32 }
++
++@"\01?buff@@3UByteAddressBuffer@@A" = external global %struct.ByteAddressBuffer, align 4
++
++; Function Attrs: nounwind
++define void @main() #0 {
++entry:
++  %0 = load %struct.ByteAddressBuffer, %struct.ByteAddressBuffer* @"\01?buff@@3UByteAddressBuffer@@A", !dbg !17 ; line:5 col:7
++  %1 = call %dx.types.Handle @"dx.hl.createhandle..%dx.types.Handle (i32, %struct.ByteAddressBuffer)"(i32 0, %struct.ByteAddressBuffer %0), !dbg !17 ; line:5 col:7
++  %2 = call %dx.types.Handle @"dx.hl.annotatehandle..%dx.types.Handle (i32, %dx.types.Handle, %dx.types.ResourceProperties, %struct.ByteAddressBuffer)"(i32 14, %dx.types.Handle %1, %dx.types.ResourceProperties { i32 11, i32 0 }, %struct.ByteAddressBuffer undef), !dbg !17 ; line:5 col:7
++  %3 = call i32 @"dx.hl.op.ro.i32 (i32, %dx.types.Handle, i32)"(i32 231, %dx.types.Handle %2, i32 0), !dbg !17 ; line:5 col:7
++  %tobool = icmp ne i32 %3, 0, !dbg !17 ; line:5 col:7
++  br i1 %tobool, label %return, label %while.body, !dbg !17 ; line:5 col:7
++
++while.body:                                       ; preds = %while.body.3, %while.body, %cleanup.46, %entry
++  %j.0 = phi i32 [ 0, %entry ], [ %j.1, %cleanup.46 ], [ %j.0, %while.body ], [ %j.1, %while.body.3 ]
++  %i.0 = phi i32 [ 0, %entry ], [ %i.1, %cleanup.46 ], [ %i.0, %while.body ], [ %i.1, %while.body.3 ]
++  %cmp = icmp slt i32 %i.0, 2, !dbg !21 ; line:12 col:17
++  %cond = icmp eq i32 %i.0, 0, !dbg !22 ; line:13 col:5
++  br i1 %cond, label %while.body.3, label %while.body, !dbg !22 ; line:13 col:5
++
++while.body.3:                                     ; preds = %cleanup.46.thread, %while.body, %cleanup.46
++  %j.1 = phi i32 [ %j.1, %cleanup.46 ], [ %j.0, %while.body ], [ %add, %cleanup.46.thread ]
++  %i.1 = phi i32 [ %i.1, %cleanup.46 ], [ %i.0, %while.body ], [ %i.1, %cleanup.46.thread ]
++  %cmp4 = icmp slt i32 %j.1, 2, !dbg !23 ; line:16 col:23
++  br i1 %cmp4, label %while.body.11, label %while.body, !dbg !24 ; line:17 col:15
++
++while.body.11:                                    ; preds = %while.body.3, %cleanup
++  br label %while.body.13, !dbg !25 ; line:23 col:13
++
++while.body.13:                                    ; preds = %while.body.13, %while.body.11
++  br i1 %cmp, label %while.body.20, label %while.body.13, !dbg !26 ; line:24 col:19
++
++while.body.20:                                    ; preds = %while.body.13, %while.end.37
++  br i1 %cmp4, label %cleanup, label %while.end.37, !dbg !27 ; line:28 col:21
++
++while.end.37:                                     ; preds = %while.body.20
++  br i1 %cmp, label %cleanup, label %while.body.20, !dbg !28 ; line:39 col:19
++
++cleanup:                                          ; preds = %while.end.37, %while.body.20
++  %cleanup.dest.slot.0 = phi i32 [ 1, %while.body.20 ], [ 8, %while.end.37 ]
++  switch i32 %cleanup.dest.slot.0, label %cleanup.46 [
++    i32 0, label %while.body.11
++    i32 8, label %cleanup.46.thread
++  ]
++
++cleanup.46.thread:                                ; preds = %cleanup
++  %add = add nsw i32 %j.1, 2, !dbg !29 ; line:43 col:18
++  br label %while.body.3
++
++cleanup.46:                                       ; preds = %cleanup
++  switch i32 %cleanup.dest.slot.0, label %return [
++    i32 0, label %while.body.3
++    i32 6, label %while.body
++  ]
++
++return:                                           ; preds = %cleanup.46, %entry
++  ret void, !dbg !30 ; line:48 col:1
++}
++
++; Function Attrs: nounwind
++declare void @llvm.lifetime.start(i64, i8* nocapture) #0
++
++; Function Attrs: nounwind
++declare void @llvm.lifetime.end(i64, i8* nocapture) #0
++
++; Function Attrs: nounwind readonly
++declare i32 @"dx.hl.op.ro.i32 (i32, %dx.types.Handle, i32)"(i32, %dx.types.Handle, i32) #1
++
++; Function Attrs: nounwind readnone
++declare %dx.types.Handle @"dx.hl.createhandle..%dx.types.Handle (i32, %struct.ByteAddressBuffer)"(i32, %struct.ByteAddressBuffer) #2
++
++; Function Attrs: nounwind readnone
++declare %dx.types.Handle @"dx.hl.annotatehandle..%dx.types.Handle (i32, %dx.types.Handle, %dx.types.ResourceProperties, %struct.ByteAddressBuffer)"(i32, %dx.types.Handle, %dx.types.ResourceProperties, %struct.ByteAddressBuffer) #2
++
++attributes #0 = { nounwind }
++attributes #1 = { nounwind readonly }
++attributes #2 = { nounwind readnone }
++
++!llvm.module.flags = !{!0}
++!pauseresume = !{!1}
++!llvm.ident = !{!2}
++!dx.version = !{!3}
++!dx.valver = !{!4}
++!dx.shaderModel = !{!5}
++!dx.typeAnnotations = !{!6}
++!dx.entryPoints = !{!10}
++!dx.fnprops = !{!14}
++!dx.options = !{!15, !16}
++
++!0 = !{i32 2, !"Debug Info Version", i32 3}
++!1 = !{!"hlsl-hlemit", !"hlsl-hlensure"}
++!2 = !{!"dxc(private) 1.8.0.4514 (d9bd2a706-dirty)"}
++!3 = !{i32 1, i32 6}
++!4 = !{i32 1, i32 8}
++!5 = !{!"cs", i32 6, i32 6}
++!6 = !{i32 1, void ()* @main, !7}
++!7 = !{!8}
++!8 = !{i32 1, !9, !9}
++!9 = !{}
++!10 = !{void ()* @main, !"main", null, !11, null}
++!11 = !{!12, null, null, null}
++!12 = !{!13}
++!13 = !{i32 0, %struct.ByteAddressBuffer* @"\01?buff@@3UByteAddressBuffer@@A", !"buff", i32 0, i32 0, i32 1, i32 11, i32 0, null}
++!14 = !{void ()* @main, i32 5, i32 1, i32 1, i32 1}
++!15 = !{i32 64}
++!16 = !{i32 -1}
++!17 = !DILocation(line: 5, column: 7, scope: !18)
++!18 = !DISubprogram(name: "main", scope: !19, file: !19, line: 4, type: !20, isLocal: false, isDefinition: true, scopeLine: 4, flags: DIFlagPrototyped, isOptimized: false, function: void ()* @main)
++!19 = !DIFile(filename: "/mnt/c/Users/amaiorano/Downloads/338103465/standalone_reduced.hlsl", directory: "")
++!20 = !DISubroutineType(types: !9)
++!21 = !DILocation(line: 12, column: 17, scope: !18)
++!22 = !DILocation(line: 13, column: 5, scope: !18)
++!23 = !DILocation(line: 16, column: 23, scope: !18)
++!24 = !DILocation(line: 17, column: 15, scope: !18)
++!25 = !DILocation(line: 23, column: 13, scope: !18)
++!26 = !DILocation(line: 24, column: 19, scope: !18)
++!27 = !DILocation(line: 28, column: 21, scope: !18)
++!28 = !DILocation(line: 39, column: 19, scope: !18)
++!29 = !DILocation(line: 43, column: 18, scope: !18)
++!30 = !DILocation(line: 48, column: 1, scope: !18)

+ 2 - 0
patches/chromium/.patches

@@ -146,3 +146,5 @@ cherry-pick-03609e39be8c.patch
 x11_use_localized_display_label_only_for_browser_process.patch
 cherry-pick-b922fcb61e3b.patch
 cherry-pick-0d9598145069.patch
+cherry-pick-24329fe5c4d0.patch
+m120-lts_mediasession_use_a_mediasessionimpl_weakptr_in.patch

+ 106 - 0
patches/chromium/cherry-pick-24329fe5c4d0.patch

@@ -0,0 +1,106 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Nidhi Jaju <[email protected]>
+Date: Fri, 24 May 2024 01:26:02 +0000
+Subject: Streams: Check if buffer is detached when filling pull-into
+ descriptor
+
+The pull-into descriptor can become out-of-sync with the array buffer
+when the buffer is detached. This CL adds a check to see if the buffer
+is detached before trying to fill it.
+
+(cherry picked from commit cd405492789ec4bc6ecd598754154c527ff60e95)
+
+Bug: 339877167
+Change-Id: Ibf46a75e36dc739910db07f2e88ff9998c21e8a8
+Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5553232
+Reviewed-by: Domenic Denicola <[email protected]>
+Commit-Queue: Nidhi Jaju <[email protected]>
+Cr-Original-Commit-Position: refs/heads/main@{#1303628}
+Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5553411
+Cr-Commit-Position: refs/branch-heads/6367@{#1228}
+Cr-Branched-From: d158c6dc6e3604e6f899041972edf26087a49740-refs/heads/main@{#1274542}
+
+diff --git a/third_party/blink/renderer/core/streams/readable_byte_stream_controller.cc b/third_party/blink/renderer/core/streams/readable_byte_stream_controller.cc
+index 85e2214ca95790f547819e2a14628d342f7913bb..a844d84d20e68172e285cefe3301c49f3edfbd3a 100644
+--- a/third_party/blink/renderer/core/streams/readable_byte_stream_controller.cc
++++ b/third_party/blink/renderer/core/streams/readable_byte_stream_controller.cc
+@@ -494,7 +494,8 @@ void ReadableByteStreamController::ProcessPullIntoDescriptorsUsingQueue(
+         controller->pending_pull_intos_[0];
+     //   c. If ! ReadableByteStreamControllerFillPullIntoDescriptorFromQueue(
+     //   controller, pullIntoDescriptor) is true,
+-    if (FillPullIntoDescriptorFromQueue(controller, pull_into_descriptor)) {
++    if (FillPullIntoDescriptorFromQueue(controller, pull_into_descriptor,
++                                        exception_state)) {
+       //     i. Perform !
+       //     ReadableByteStreamControllerShiftPendingPullInto(controller).
+       ShiftPendingPullInto(controller);
+@@ -505,6 +506,15 @@ void ReadableByteStreamController::ProcessPullIntoDescriptorsUsingQueue(
+                                pull_into_descriptor, exception_state);
+       DCHECK(!exception_state.HadException());
+     }
++    if (exception_state.HadException()) {
++      // Instead of returning a rejection, which is inconvenient here,
++      // call ControllerError(). The only difference this makes is that it
++      // happens synchronously, but that should not be observable.
++      ReadableByteStreamController::Error(script_state, controller,
++                                          exception_state.GetException());
++      exception_state.ClearException();
++      return;
++    }
+   }
+ }
+ 
+@@ -989,7 +999,12 @@ void ReadableByteStreamController::FillHeadPullIntoDescriptor(
+ 
+ bool ReadableByteStreamController::FillPullIntoDescriptorFromQueue(
+     ReadableByteStreamController* controller,
+-    PullIntoDescriptor* pull_into_descriptor) {
++    PullIntoDescriptor* pull_into_descriptor,
++    ExceptionState& exception_state) {
++  if (pull_into_descriptor->buffer->IsDetached()) {
++    exception_state.ThrowTypeError("buffer is detached");
++    return false;
++  }
+   // https://streams.spec.whatwg.org/#readable-byte-stream-controller-fill-pull-into-descriptor-from-queue
+   // 1. Let elementSize be pullIntoDescriptor.[[elementSize]].
+   const size_t element_size = pull_into_descriptor->element_size;
+@@ -1240,7 +1255,8 @@ void ReadableByteStreamController::PullInto(
+     //   a. If !
+     //   ReadableByteStreamControllerFillPullIntoDescriptorFromQueue(controller,
+     //   pullIntoDescriptor) is true,
+-    if (FillPullIntoDescriptorFromQueue(controller, pull_into_descriptor)) {
++    if (FillPullIntoDescriptorFromQueue(controller, pull_into_descriptor,
++                                        exception_state)) {
+       //     i. Let filledView be !
+       //     ReadableByteStreamControllerConvertPullIntoDescriptor(pullIntoDescriptor).
+       DOMArrayBufferView* filled_view = ConvertPullIntoDescriptor(
+@@ -1254,6 +1270,15 @@ void ReadableByteStreamController::PullInto(
+       //     iv. Return.
+       return;
+     }
++    if (exception_state.HadException()) {
++      // Instead of returning a rejection, which is inconvenient here,
++      // call ControllerError(). The only difference this makes is that it
++      // happens synchronously, but that should not be observable.
++      ReadableByteStreamController::Error(script_state, controller,
++                                          exception_state.GetException());
++      exception_state.ClearException();
++      return;
++    }
+     //   b. If controller.[[closeRequested]] is true,
+     if (controller->close_requested_) {
+       //     i. Let e be a TypeError exception.
+diff --git a/third_party/blink/renderer/core/streams/readable_byte_stream_controller.h b/third_party/blink/renderer/core/streams/readable_byte_stream_controller.h
+index aff7d589cef5a32f43e1dc0b06aa0d2921e39169..f31f660fddbc01d95dff904ad9ac5b1cf3ee8d86 100644
+--- a/third_party/blink/renderer/core/streams/readable_byte_stream_controller.h
++++ b/third_party/blink/renderer/core/streams/readable_byte_stream_controller.h
+@@ -218,7 +218,8 @@ class CORE_EXPORT ReadableByteStreamController
+ 
+   // https://streams.spec.whatwg.org/#readable-byte-stream-controller-fill-pull-into-descriptor-from-queue
+   static bool FillPullIntoDescriptorFromQueue(ReadableByteStreamController*,
+-                                              PullIntoDescriptor*);
++                                              PullIntoDescriptor*,
++                                              ExceptionState&);
+ 
+   // https://streams.spec.whatwg.org/#abstract-opdef-readablebytestreamcontrollerfillreadrequestfromqueue
+   static void FillReadRequestFromQueue(ScriptState*,

+ 207 - 0
patches/chromium/m120-lts_mediasession_use_a_mediasessionimpl_weakptr_in.patch

@@ -0,0 +1,207 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Zakhar Voit <[email protected]>
+Date: Fri, 7 Jun 2024 11:26:03 +0000
+Subject: MediaSession: Use a MediaSessionImpl WeakPtr in
+ MediaSessionServiceImpl
+
+Currently, every time MediaSessionServiceImpl wants to talk to its
+associated MediaSessionImpl, it recalculates it from its
+RenderFrameHostId. This can lead to issues where a
+MediaSessionServiceImpl of a disconnected RenderFrameHost can no longer
+access the MediaSessionImpl to tell it that it is being deleted,
+leaving MediaSessionImpl with a dangling raw_ptr.
+
+(cherry picked from commit 1f0de3303671c6c041930c7f4f8a9ad017a7f211)
+
+(cherry picked from commit 11c5f7911caab6930812a515eac27e35776ba35c)
+
+Bug: 338929744
+Change-Id: I092d217d4a975b67a84280687ed5461a14ead98a
+Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5577944
+Commit-Queue: Tommy Steimel <[email protected]>
+Cr-Original-Commit-Position: refs/branch-heads/6367@{#1245}
+Cr-Original-Branched-From: d158c6dc6e3604e6f899041972edf26087a49740-refs/heads/main@{#1274542}
+Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5583452
+Owners-Override: Michael Ershov <[email protected]>
+Commit-Queue: Michael Ershov <[email protected]>
+Reviewed-by: Dale Curtis <[email protected]>
+Reviewed-by: Michael Ershov <[email protected]>
+Cr-Commit-Position: refs/branch-heads/6099@{#2035}
+Cr-Branched-From: e6ee4500f7d6549a9ac1354f8d056da49ef406be-refs/heads/main@{#1217362}
+
+diff --git a/content/browser/media/session/media_session_impl.cc b/content/browser/media/session/media_session_impl.cc
+index 5ccef5240eef2e3b5c82bc50b97331e1b2fb9f31..710aeb26aee5bcfd95fef96fc7b8be7aa27173ad 100644
+--- a/content/browser/media/session/media_session_impl.cc
++++ b/content/browser/media/session/media_session_impl.cc
+@@ -1669,6 +1669,10 @@ const base::UnguessableToken& MediaSessionImpl::GetRequestId() const {
+   return delegate_->request_id();
+ }
+ 
++base::WeakPtr<MediaSessionImpl> MediaSessionImpl::GetWeakPtr() {
++  return weak_factory_.GetWeakPtr();
++}
++
+ void MediaSessionImpl::RebuildAndNotifyActionsChanged() {
+   std::set<media_session::mojom::MediaSessionAction> actions =
+       routed_service_ ? routed_service_->actions()
+diff --git a/content/browser/media/session/media_session_impl.h b/content/browser/media/session/media_session_impl.h
+index 1f30f99fdd94617e3ddb8fb701c01201fbebf9df..af0f967b45e52778837ea716bc8290c6c0e20a6a 100644
+--- a/content/browser/media/session/media_session_impl.h
++++ b/content/browser/media/session/media_session_impl.h
+@@ -17,6 +17,7 @@
+ #include "base/containers/id_map.h"
+ #include "base/memory/raw_ptr.h"
+ #include "base/memory/raw_ptr_exclusion.h"
++#include "base/memory/weak_ptr.h"
+ #include "base/timer/timer.h"
+ #include "build/build_config.h"
+ #include "content/browser/media/session/audio_focus_delegate.h"
+@@ -346,6 +347,9 @@ class MediaSessionImpl : public MediaSession,
+   // Returns the Audio Focus request ID associated with this media session.
+   const base::UnguessableToken& GetRequestId() const;
+ 
++  // Returns a WeakPtr to `this`.
++  base::WeakPtr<MediaSessionImpl> GetWeakPtr();
++
+   CONTENT_EXPORT bool HasImageCacheForTest(const GURL& image_url) const;
+ 
+   // Make sure that all observers have received any pending callbacks from us,
+@@ -641,6 +645,8 @@ class MediaSessionImpl : public MediaSession,
+ 
+   media_session::mojom::RemotePlaybackMetadataPtr remote_playback_metadata_;
+ 
++  base::WeakPtrFactory<MediaSessionImpl> weak_factory_{this};
++
+   WEB_CONTENTS_USER_DATA_KEY_DECL();
+ };
+ 
+diff --git a/content/browser/media/session/media_session_service_impl.cc b/content/browser/media/session/media_session_service_impl.cc
+index 532d1161b5321fbe37552f1caca2d20782356f36..a3ca009421a22d51a9d85f4665dd769319d26c22 100644
+--- a/content/browser/media/session/media_session_service_impl.cc
++++ b/content/browser/media/session/media_session_service_impl.cc
+@@ -22,14 +22,16 @@ MediaSessionServiceImpl::MediaSessionServiceImpl(
+     : render_frame_host_id_(render_frame_host->GetGlobalId()),
+       playback_state_(blink::mojom::MediaSessionPlaybackState::NONE) {
+   MediaSessionImpl* session = GetMediaSession();
+-  if (session)
+-    session->OnServiceCreated(this);
++  if (session) {
++    media_session_ = session->GetWeakPtr();
++    media_session_->OnServiceCreated(this);
++  }
+ }
+ 
+ MediaSessionServiceImpl::~MediaSessionServiceImpl() {
+-  MediaSessionImpl* session = GetMediaSession();
+-  if (session)
+-    session->OnServiceDestroyed(this);
++  if (media_session_) {
++    media_session_->OnServiceDestroyed(this);
++  }
+ }
+ 
+ // static
+@@ -70,17 +72,17 @@ void MediaSessionServiceImpl::SetClient(
+ void MediaSessionServiceImpl::SetPlaybackState(
+     blink::mojom::MediaSessionPlaybackState state) {
+   playback_state_ = state;
+-  MediaSessionImpl* session = GetMediaSession();
+-  if (session)
+-    session->OnMediaSessionPlaybackStateChanged(this);
++  if (media_session_) {
++    media_session_->OnMediaSessionPlaybackStateChanged(this);
++  }
+ }
+ 
+ void MediaSessionServiceImpl::SetPositionState(
+     const std::optional<media_session::MediaPosition>& position) {
+   position_ = position;
+-  MediaSessionImpl* session = GetMediaSession();
+-  if (session)
+-    session->RebuildAndNotifyMediaPositionChanged();
++  if (media_session_) {
++    media_session_->RebuildAndNotifyMediaPositionChanged();
++  }
+ }
+ 
+ void MediaSessionServiceImpl::SetMetadata(
+@@ -102,48 +104,48 @@ void MediaSessionServiceImpl::SetMetadata(
+     metadata_ = std::move(metadata);
+   }
+ 
+-  MediaSessionImpl* session = GetMediaSession();
+-  if (session)
+-    session->OnMediaSessionMetadataChanged(this);
++  if (media_session_) {
++    media_session_->OnMediaSessionMetadataChanged(this);
++  }
+ }
+ 
+ void MediaSessionServiceImpl::SetMicrophoneState(
+     media_session::mojom::MicrophoneState microphone_state) {
+   microphone_state_ = microphone_state;
+-  MediaSessionImpl* session = GetMediaSession();
+-  if (session)
+-    session->OnMediaSessionInfoChanged(this);
++  if (media_session_) {
++    media_session_->OnMediaSessionInfoChanged(this);
++  }
+ }
+ 
+ void MediaSessionServiceImpl::SetCameraState(
+     media_session::mojom::CameraState camera_state) {
+   camera_state_ = camera_state;
+-  MediaSessionImpl* session = GetMediaSession();
+-  if (session)
+-    session->OnMediaSessionInfoChanged(this);
++  if (media_session_) {
++    media_session_->OnMediaSessionInfoChanged(this);
++  }
+ }
+ 
+ void MediaSessionServiceImpl::EnableAction(
+     media_session::mojom::MediaSessionAction action) {
+   actions_.insert(action);
+-  MediaSessionImpl* session = GetMediaSession();
+-  if (session)
+-    session->OnMediaSessionActionsChanged(this);
++  if (media_session_) {
++    media_session_->OnMediaSessionActionsChanged(this);
++  }
+ }
+ 
+ void MediaSessionServiceImpl::DisableAction(
+     media_session::mojom::MediaSessionAction action) {
+   actions_.erase(action);
+-  MediaSessionImpl* session = GetMediaSession();
+-  if (session)
+-    session->OnMediaSessionActionsChanged(this);
++  if (media_session_) {
++    media_session_->OnMediaSessionActionsChanged(this);
++  }
+ }
+ 
+ void MediaSessionServiceImpl::ClearActions() {
+   actions_.clear();
+-  MediaSessionImpl* session = GetMediaSession();
+-  if (session)
+-    session->OnMediaSessionActionsChanged(this);
++  if (media_session_) {
++    media_session_->OnMediaSessionActionsChanged(this);
++  }
+ }
+ 
+ MediaSessionImpl* MediaSessionServiceImpl::GetMediaSession() {
+diff --git a/content/browser/media/session/media_session_service_impl.h b/content/browser/media/session/media_session_service_impl.h
+index 4eeffe2a8bbc532d15e5deb7bc77eebea41326cf..514c043648e70b3c29a57ddc5faabaf85e103491 100644
+--- a/content/browser/media/session/media_session_service_impl.h
++++ b/content/browser/media/session/media_session_service_impl.h
+@@ -85,6 +85,8 @@ class CONTENT_EXPORT MediaSessionServiceImpl
+ 
+   const GlobalRenderFrameHostId render_frame_host_id_;
+ 
++  base::WeakPtr<MediaSessionImpl> media_session_;
++
+   mojo::Remote<blink::mojom::MediaSessionClient> client_;
+   blink::mojom::MediaSessionPlaybackState playback_state_;
+   blink::mojom::SpecMediaMetadataPtr metadata_;

+ 2 - 1
patches/config.json

@@ -13,5 +13,6 @@
   { "patch_dir": "src/electron/patches/webrtc", "repo": "src/third_party/webrtc" },
   { "patch_dir": "src/electron/patches/reclient-configs", "repo": "src/third_party/engflow-reclient-configs" },
   { "patch_dir": "src/electron/patches/angle", "repo": "src/third_party/angle" },
-  { "patch_dir": "src/electron/patches/DirectXShaderCompiler", "repo": "src/third_party/dawn/third_party/dxc" }
+  { "patch_dir": "src/electron/patches/DirectXShaderCompiler", "repo": "src/third_party/dawn/third_party/dxc" },
+  { "patch_dir": "src/electron/patches/libaom", "repo": "src/third_party/libaom/source/libaom"}
 ]

+ 2 - 0
patches/libaom/.patches

@@ -0,0 +1,2 @@
+update_codec_config_after_svc_scale_controls.patch
+encode_api_test_add_repro_for_chromium_339877165.patch

+ 162 - 0
patches/libaom/encode_api_test_add_repro_for_chromium_339877165.patch

@@ -0,0 +1,162 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: James Zern <[email protected]>
+Date: Thu, 16 May 2024 13:44:52 -0700
+Subject: encode_api_test: add repro for chromium 339877165
+
+BUG=chromium:339877165
+
+Change-Id: I69dcc2cda098ec96a34e1e5f7ef557ee8caf5521
+(cherry picked from commit 01467cdbd524900eed283660836179fd1b2cd536)
+
+diff --git a/test/encode_api_test.cc b/test/encode_api_test.cc
+index 605743f9be8ccc776aa3b8dcae0a79e7dc6711e6..c0a79fe734e7985b52bdbaaa5d8dec2c541275e5 100644
+--- a/test/encode_api_test.cc
++++ b/test/encode_api_test.cc
+@@ -556,6 +556,147 @@ TEST(EncodeAPI, Buganizer310457427) {
+   encoder.Encode(false);
+ }
+ 
++// Reproduces https://crbug.com/339877165.
++TEST(EncodeAPI, Buganizer339877165) {
++  // Initialize libaom encoder.
++  aom_codec_iface_t *const iface = aom_codec_av1_cx();
++  aom_codec_ctx_t enc;
++  aom_codec_enc_cfg_t cfg;
++
++  ASSERT_EQ(aom_codec_enc_config_default(iface, &cfg, AOM_USAGE_REALTIME),
++            AOM_CODEC_OK);
++
++  cfg.g_w = 2560;
++  cfg.g_h = 1600;
++  cfg.rc_target_bitrate = 231;
++  cfg.rc_end_usage = AOM_CBR;
++  cfg.g_threads = 8;
++
++  ASSERT_EQ(aom_codec_enc_init(&enc, iface, &cfg, 0), AOM_CODEC_OK);
++
++  // From libaom_av1_encoder.cc in WebRTC.
++  ASSERT_EQ(aom_codec_control(&enc, AOME_SET_CPUUSED, 11), AOM_CODEC_OK);
++  ASSERT_EQ(aom_codec_control(&enc, AV1E_SET_ENABLE_CDEF, 1), AOM_CODEC_OK);
++  ASSERT_EQ(aom_codec_control(&enc, AV1E_SET_ENABLE_TPL_MODEL, 0),
++            AOM_CODEC_OK);
++  ASSERT_EQ(aom_codec_control(&enc, AV1E_SET_DELTAQ_MODE, 0), AOM_CODEC_OK);
++  ASSERT_EQ(aom_codec_control(&enc, AV1E_SET_ENABLE_ORDER_HINT, 0),
++            AOM_CODEC_OK);
++  ASSERT_EQ(aom_codec_control(&enc, AV1E_SET_AQ_MODE, 3), AOM_CODEC_OK);
++  ASSERT_EQ(aom_codec_control(&enc, AOME_SET_MAX_INTRA_BITRATE_PCT, 300),
++            AOM_CODEC_OK);
++  ASSERT_EQ(aom_codec_control(&enc, AV1E_SET_COEFF_COST_UPD_FREQ, 3),
++            AOM_CODEC_OK);
++  ASSERT_EQ(aom_codec_control(&enc, AV1E_SET_MODE_COST_UPD_FREQ, 3),
++            AOM_CODEC_OK);
++  ASSERT_EQ(aom_codec_control(&enc, AV1E_SET_MV_COST_UPD_FREQ, 3),
++            AOM_CODEC_OK);
++  ASSERT_EQ(aom_codec_control(&enc, AV1E_SET_TUNE_CONTENT, AOM_CONTENT_SCREEN),
++            AOM_CODEC_OK);
++  ASSERT_EQ(aom_codec_control(&enc, AV1E_SET_ENABLE_PALETTE, 1), AOM_CODEC_OK);
++  ASSERT_EQ(aom_codec_control(&enc, AV1E_SET_TILE_ROWS, 1), AOM_CODEC_OK);
++  ASSERT_EQ(aom_codec_control(&enc, AV1E_SET_TILE_COLUMNS, 2), AOM_CODEC_OK);
++  ASSERT_EQ(aom_codec_control(&enc, AV1E_SET_ENABLE_OBMC, 0), AOM_CODEC_OK);
++  ASSERT_EQ(aom_codec_control(&enc, AV1E_SET_NOISE_SENSITIVITY, 0),
++            AOM_CODEC_OK);
++  ASSERT_EQ(aom_codec_control(&enc, AV1E_SET_ENABLE_WARPED_MOTION, 0),
++            AOM_CODEC_OK);
++  ASSERT_EQ(aom_codec_control(&enc, AV1E_SET_ENABLE_GLOBAL_MOTION, 0),
++            AOM_CODEC_OK);
++  ASSERT_EQ(aom_codec_control(&enc, AV1E_SET_ENABLE_REF_FRAME_MVS, 0),
++            AOM_CODEC_OK);
++  ASSERT_EQ(aom_codec_control(&enc, AV1E_SET_SUPERBLOCK_SIZE,
++                              AOM_SUPERBLOCK_SIZE_DYNAMIC),
++            AOM_CODEC_OK);
++  ASSERT_EQ(aom_codec_control(&enc, AV1E_SET_ENABLE_CFL_INTRA, 0),
++            AOM_CODEC_OK);
++  ASSERT_EQ(aom_codec_control(&enc, AV1E_SET_ENABLE_SMOOTH_INTRA, 0),
++            AOM_CODEC_OK);
++  ASSERT_EQ(aom_codec_control(&enc, AV1E_SET_ENABLE_ANGLE_DELTA, 0),
++            AOM_CODEC_OK);
++  ASSERT_EQ(aom_codec_control(&enc, AV1E_SET_ENABLE_FILTER_INTRA, 0),
++            AOM_CODEC_OK);
++  ASSERT_EQ(aom_codec_control(&enc, AV1E_SET_INTRA_DEFAULT_TX_ONLY, 1),
++            AOM_CODEC_OK);
++  ASSERT_EQ(aom_codec_control(&enc, AV1E_SET_DISABLE_TRELLIS_QUANT, 1),
++            AOM_CODEC_OK);
++  ASSERT_EQ(aom_codec_control(&enc, AV1E_SET_ENABLE_DIST_WTD_COMP, 0),
++            AOM_CODEC_OK);
++  ASSERT_EQ(aom_codec_control(&enc, AV1E_SET_ENABLE_DIFF_WTD_COMP, 0),
++            AOM_CODEC_OK);
++  ASSERT_EQ(aom_codec_control(&enc, AV1E_SET_ENABLE_DUAL_FILTER, 0),
++            AOM_CODEC_OK);
++  ASSERT_EQ(aom_codec_control(&enc, AV1E_SET_ENABLE_INTERINTRA_COMP, 0),
++            AOM_CODEC_OK);
++  ASSERT_EQ(aom_codec_control(&enc, AV1E_SET_ENABLE_INTERINTRA_WEDGE, 0),
++            AOM_CODEC_OK);
++  ASSERT_EQ(aom_codec_control(&enc, AV1E_SET_ENABLE_INTRA_EDGE_FILTER, 0),
++            AOM_CODEC_OK);
++  ASSERT_EQ(aom_codec_control(&enc, AV1E_SET_ENABLE_INTRABC, 0), AOM_CODEC_OK);
++  ASSERT_EQ(aom_codec_control(&enc, AV1E_SET_ENABLE_MASKED_COMP, 0),
++            AOM_CODEC_OK);
++  ASSERT_EQ(aom_codec_control(&enc, AV1E_SET_ENABLE_PAETH_INTRA, 0),
++            AOM_CODEC_OK);
++  ASSERT_EQ(aom_codec_control(&enc, AV1E_SET_ENABLE_QM, 0), AOM_CODEC_OK);
++  ASSERT_EQ(aom_codec_control(&enc, AV1E_SET_ENABLE_RECT_PARTITIONS, 0),
++            AOM_CODEC_OK);
++  ASSERT_EQ(aom_codec_control(&enc, AV1E_SET_ENABLE_RESTORATION, 0),
++            AOM_CODEC_OK);
++  ASSERT_EQ(aom_codec_control(&enc, AV1E_SET_ENABLE_SMOOTH_INTERINTRA, 0),
++            AOM_CODEC_OK);
++  ASSERT_EQ(aom_codec_control(&enc, AV1E_SET_ENABLE_TX64, 0), AOM_CODEC_OK);
++  ASSERT_EQ(aom_codec_control(&enc, AV1E_SET_MAX_REFERENCE_FRAMES, 3),
++            AOM_CODEC_OK);
++  ASSERT_EQ(aom_codec_enc_config_set(&enc, &cfg), AOM_CODEC_OK);
++
++  aom_svc_params_t svc_params = {};
++  svc_params.number_spatial_layers = 2;
++  svc_params.number_temporal_layers = 1;
++  svc_params.max_quantizers[0] = svc_params.max_quantizers[1] = 56;
++  svc_params.min_quantizers[0] = svc_params.min_quantizers[1] = 10;
++  svc_params.scaling_factor_num[0] = svc_params.scaling_factor_num[1] = 1;
++  svc_params.scaling_factor_den[0] = 2;
++  svc_params.scaling_factor_den[1] = 1;
++  svc_params.layer_target_bitrate[0] = cfg.rc_target_bitrate;
++  svc_params.framerate_factor[0] = 1;
++  ASSERT_EQ(aom_codec_control(&enc, AV1E_SET_SVC_PARAMS, &svc_params),
++            AOM_CODEC_OK);
++
++  aom_svc_layer_id_t layer_id = {};
++  ASSERT_EQ(aom_codec_control(&enc, AV1E_SET_SVC_LAYER_ID, &layer_id),
++            AOM_CODEC_OK);
++
++  aom_svc_ref_frame_config_t ref_frame_config = {};
++  ref_frame_config.refresh[0] = 1;
++  ASSERT_EQ(
++      aom_codec_control(&enc, AV1E_SET_SVC_REF_FRAME_CONFIG, &ref_frame_config),
++      AOM_CODEC_OK);
++
++  // Create input image.
++  aom_image_t *const image =
++      CreateGrayImage(AOM_IMG_FMT_I420, cfg.g_w, cfg.g_h);
++  ASSERT_NE(image, nullptr);
++
++  // Encode layer 0.
++  ASSERT_EQ(aom_codec_encode(&enc, image, 0, 1, 0), AOM_CODEC_OK);
++
++  layer_id.spatial_layer_id = 1;
++  ASSERT_EQ(aom_codec_control(&enc, AV1E_SET_SVC_LAYER_ID, &layer_id),
++            AOM_CODEC_OK);
++
++  ref_frame_config.refresh[0] = 0;
++  ASSERT_EQ(
++      aom_codec_control(&enc, AV1E_SET_SVC_REF_FRAME_CONFIG, &ref_frame_config),
++      AOM_CODEC_OK);
++
++  // Encode layer 1.
++  ASSERT_EQ(aom_codec_encode(&enc, image, 0, 1, 0), AOM_CODEC_OK);
++
++  // Free resources.
++  aom_img_free(image);
++  aom_codec_destroy(&enc);
++}
++
+ class EncodeAPIParameterized
+     : public testing::TestWithParam<std::tuple<
+           /*usage=*/unsigned int, /*speed=*/int, /*aq_mode=*/unsigned int>> {};

+ 196 - 0
patches/libaom/update_codec_config_after_svc_scale_controls.patch

@@ -0,0 +1,196 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: James Zern <[email protected]>
+Date: Tue, 14 May 2024 17:54:10 -0700
+Subject: update codec config after svc/scale controls
+
+This ensures the encoder state/allocations stay in sync with scaling and
+svc layer changes. In the SVC case, depending on the resolution,
+differences in the chosen superblock size among layers may have caused a
+crash. This was reproducible in WebRTC in screen content mode.
+
+The fix is based on a change by Yuan Tong (tongyuan200097) [1]. It
+refreshes the encoder config after AOME_SET_SCALEMODE,
+AOME_SET_NUMBER_SPATIAL_LAYERS and AV1E_SET_SVC_PARAMS if no frames have
+been encoded. AV1E_SET_SVC_PARAMS was missed in the original change.
+
+[1]: https://aomedia-review.googlesource.com/c/aom/+/171941/2
+
+Bug: chromium:339877165
+Change-Id: Ib3d2a123b159898d7c7e19c81e89ff148920e1f1
+(cherry picked from commit e42f4b1980bbbc772aa886d8b43a885461d7b89e)
+
+diff --git a/av1/av1_cx_iface.c b/av1/av1_cx_iface.c
+index 9214feb4e6f9dd068444e76bf8073a0bbe772134..68d6de21845a4e635f67f0a972126563d8f4fb7c 100644
+--- a/av1/av1_cx_iface.c
++++ b/av1/av1_cx_iface.c
+@@ -1602,37 +1602,42 @@ static aom_codec_err_t ctrl_get_baseline_gf_interval(aom_codec_alg_priv_t *ctx,
+   return AOM_CODEC_OK;
+ }
+ 
++static aom_codec_err_t update_encoder_cfg(aom_codec_alg_priv_t *ctx) {
++  set_encoder_config(&ctx->oxcf, &ctx->cfg, &ctx->extra_cfg);
++  av1_check_fpmt_config(ctx->ppi, &ctx->oxcf);
++  bool is_sb_size_changed = false;
++  av1_change_config_seq(ctx->ppi, &ctx->oxcf, &is_sb_size_changed);
++  for (int i = 0; i < ctx->ppi->num_fp_contexts; i++) {
++    AV1_COMP *const cpi = ctx->ppi->parallel_cpi[i];
++    struct aom_internal_error_info *const error = cpi->common.error;
++    if (setjmp(error->jmp)) {
++      error->setjmp = 0;
++      return error->error_code;
++    }
++    error->setjmp = 1;
++    av1_change_config(cpi, &ctx->oxcf, is_sb_size_changed);
++    error->setjmp = 0;
++  }
++  if (ctx->ppi->cpi_lap != NULL) {
++    AV1_COMP *const cpi_lap = ctx->ppi->cpi_lap;
++    struct aom_internal_error_info *const error = cpi_lap->common.error;
++    if (setjmp(error->jmp)) {
++      error->setjmp = 0;
++      return error->error_code;
++    }
++    error->setjmp = 1;
++    av1_change_config(cpi_lap, &ctx->oxcf, is_sb_size_changed);
++    error->setjmp = 0;
++  }
++  return AOM_CODEC_OK;
++}
++
+ static aom_codec_err_t update_extra_cfg(aom_codec_alg_priv_t *ctx,
+                                         const struct av1_extracfg *extra_cfg) {
+   const aom_codec_err_t res = validate_config(ctx, &ctx->cfg, extra_cfg);
+   if (res == AOM_CODEC_OK) {
+     ctx->extra_cfg = *extra_cfg;
+-    set_encoder_config(&ctx->oxcf, &ctx->cfg, &ctx->extra_cfg);
+-    av1_check_fpmt_config(ctx->ppi, &ctx->oxcf);
+-    bool is_sb_size_changed = false;
+-    av1_change_config_seq(ctx->ppi, &ctx->oxcf, &is_sb_size_changed);
+-    for (int i = 0; i < ctx->ppi->num_fp_contexts; i++) {
+-      AV1_COMP *const cpi = ctx->ppi->parallel_cpi[i];
+-      struct aom_internal_error_info *const error = cpi->common.error;
+-      if (setjmp(error->jmp)) {
+-        error->setjmp = 0;
+-        return error->error_code;
+-      }
+-      error->setjmp = 1;
+-      av1_change_config(cpi, &ctx->oxcf, is_sb_size_changed);
+-      error->setjmp = 0;
+-    }
+-    if (ctx->ppi->cpi_lap != NULL) {
+-      AV1_COMP *const cpi_lap = ctx->ppi->cpi_lap;
+-      struct aom_internal_error_info *const error = cpi_lap->common.error;
+-      if (setjmp(error->jmp)) {
+-        error->setjmp = 0;
+-        return error->error_code;
+-      }
+-      error->setjmp = 1;
+-      av1_change_config(cpi_lap, &ctx->oxcf, is_sb_size_changed);
+-      error->setjmp = 0;
+-    }
++    return update_encoder_cfg(ctx);
+   }
+   return res;
+ }
+@@ -3573,11 +3578,23 @@ static aom_codec_err_t ctrl_set_scale_mode(aom_codec_alg_priv_t *ctx,
+   aom_scaling_mode_t *const mode = va_arg(args, aom_scaling_mode_t *);
+ 
+   if (mode) {
+-    const int res = av1_set_internal_size(
+-        &ctx->ppi->cpi->oxcf, &ctx->ppi->cpi->resize_pending_params,
+-        mode->h_scaling_mode, mode->v_scaling_mode);
+-    av1_check_fpmt_config(ctx->ppi, &ctx->ppi->cpi->oxcf);
+-    return (res == 0) ? AOM_CODEC_OK : AOM_CODEC_INVALID_PARAM;
++    AV1EncoderConfig *const oxcf =
++        ctx->ppi->seq_params_locked ? &ctx->ppi->cpi->oxcf : &ctx->oxcf;
++    const int res =
++        av1_set_internal_size(oxcf, &ctx->ppi->cpi->resize_pending_params,
++                              mode->h_scaling_mode, mode->v_scaling_mode);
++    if (res == 0) {
++      // update_encoder_cfg() is somewhat costly and this control may be called
++      // multiple times, so update_encoder_cfg() is only called to ensure frame
++      // and superblock sizes are updated before they're fixed by the first
++      // encode call.
++      if (ctx->ppi->seq_params_locked) {
++        av1_check_fpmt_config(ctx->ppi, &ctx->ppi->cpi->oxcf);
++        return AOM_CODEC_OK;
++      }
++      return update_encoder_cfg(ctx);
++    }
++    return AOM_CODEC_INVALID_PARAM;
+   } else {
+     return AOM_CODEC_INVALID_PARAM;
+   }
+@@ -3598,6 +3615,13 @@ static aom_codec_err_t ctrl_set_number_spatial_layers(aom_codec_alg_priv_t *ctx,
+   if (number_spatial_layers > MAX_NUM_SPATIAL_LAYERS)
+     return AOM_CODEC_INVALID_PARAM;
+   ctx->ppi->number_spatial_layers = number_spatial_layers;
++  // update_encoder_cfg() is somewhat costly and this control may be called
++  // multiple times, so update_encoder_cfg() is only called to ensure frame and
++  // superblock sizes are updated before they're fixed by the first encode
++  // call.
++  if (!ctx->ppi->seq_params_locked) {
++    return update_encoder_cfg(ctx);
++  }
+   return AOM_CODEC_OK;
+ }
+ 
+@@ -3615,8 +3639,6 @@ static aom_codec_err_t ctrl_set_svc_params(aom_codec_alg_priv_t *ctx,
+                                            va_list args) {
+   AV1_PRIMARY *const ppi = ctx->ppi;
+   AV1_COMP *const cpi = ppi->cpi;
+-  AV1_COMMON *const cm = &cpi->common;
+-  AV1EncoderConfig *oxcf = &cpi->oxcf;
+   aom_svc_params_t *const params = va_arg(args, aom_svc_params_t *);
+   int64_t target_bandwidth = 0;
+   ppi->number_spatial_layers = params->number_spatial_layers;
+@@ -3656,19 +3678,38 @@ static aom_codec_err_t ctrl_set_svc_params(aom_codec_alg_priv_t *ctx,
+           target_bandwidth += lc->layer_target_bitrate;
+       }
+     }
+-    if (cm->current_frame.frame_number == 0) {
+-      if (!cpi->ppi->seq_params_locked) {
+-        SequenceHeader *const seq_params = &ppi->seq_params;
+-        seq_params->operating_points_cnt_minus_1 =
+-            ppi->number_spatial_layers * ppi->number_temporal_layers - 1;
+-        av1_init_seq_coding_tools(ppi, &cpi->oxcf, 1);
+-      }
++
++    if (ppi->seq_params_locked) {
++      AV1EncoderConfig *const oxcf = &cpi->oxcf;
++      // Keep ctx->oxcf in sync in case further codec controls are made prior
++      // to encoding.
++      ctx->oxcf.rc_cfg.target_bandwidth = oxcf->rc_cfg.target_bandwidth =
++          target_bandwidth;
++      set_primary_rc_buffer_sizes(oxcf, ppi);
++      av1_update_layer_context_change_config(cpi, target_bandwidth);
++      check_reset_rc_flag(cpi);
++    } else {
++      // Note av1_init_layer_context() relies on cpi->oxcf. The order of that
++      // call and the ones in the other half of this block (which
++      // update_encoder_cfg() transitively makes) is important. So we keep
++      // ctx->oxcf and cpi->oxcf in sync here as update_encoder_cfg() will
++      // overwrite cpi->oxcf with ctx->oxcf.
++      ctx->oxcf.rc_cfg.target_bandwidth = cpi->oxcf.rc_cfg.target_bandwidth =
++          target_bandwidth;
++      SequenceHeader *const seq_params = &ppi->seq_params;
++      seq_params->operating_points_cnt_minus_1 =
++          ppi->number_spatial_layers * ppi->number_temporal_layers - 1;
++
+       av1_init_layer_context(cpi);
++      // update_encoder_cfg() is somewhat costly and this control may be called
++      // multiple times, so update_encoder_cfg() is only called to ensure frame
++      // and superblock sizes are updated before they're fixed by the first
++      // encode call.
++      return update_encoder_cfg(ctx);
+     }
+-    oxcf->rc_cfg.target_bandwidth = target_bandwidth;
+-    set_primary_rc_buffer_sizes(oxcf, cpi->ppi);
+-    av1_update_layer_context_change_config(cpi, target_bandwidth);
+-    check_reset_rc_flag(cpi);
++  } else if (!ppi->seq_params_locked) {
++    // Ensure frame and superblock sizes are updated.
++    return update_encoder_cfg(ctx);
+   }
+   av1_check_fpmt_config(ctx->ppi, &ctx->ppi->cpi->oxcf);
+   return AOM_CODEC_OK;

+ 0 - 0
patches/libvpx/.patches


+ 1 - 0
patches/v8/.patches

@@ -6,3 +6,4 @@ cherry-pick-b3c01ac1e60a.patch
 cherry-pick-6503a987d966.patch
 cherry-pick-3e037e195e50.patch
 cherry-pick-e7b64c6ee185.patch
+cherry-pick-f911ff372723.patch

+ 45 - 0
patches/v8/cherry-pick-f911ff372723.patch

@@ -0,0 +1,45 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Shu-yu Guo <[email protected]>
+Date: Fri, 10 May 2024 12:08:04 -0700
+Subject: Merged: [ic] Use slow stub element handler for non-JSObjects
+
+Fixed: 339736513
+(cherry picked from commit 8a69c7880844ab00ee2f32079579a040a87eedca)
+
+Change-Id: If87462eb044c194798a32cb25a5f3648ff823196
+Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/5555847
+Commit-Queue: Shu-yu Guo <[email protected]>
+Reviewed-by: Adam Klein <[email protected]>
+Commit-Queue: Adam Klein <[email protected]>
+Auto-Submit: Shu-yu Guo <[email protected]>
+Cr-Commit-Position: refs/branch-heads/12.4@{#36}
+Cr-Branched-From: 309640da62fae0485c7e4f64829627c92d53b35d-refs/heads/12.4.254@{#1}
+Cr-Branched-From: 5dc24701432278556a9829d27c532f974643e6df-refs/heads/main@{#92862}
+
+diff --git a/src/ic/ic.cc b/src/ic/ic.cc
+index 61bb82aff98e4273b651801b875416c686de872e..34cf7eb6d2f5ef74b37103a6ee2356b8825d3f58 100644
+--- a/src/ic/ic.cc
++++ b/src/ic/ic.cc
+@@ -2349,15 +2349,16 @@ Handle<Object> KeyedStoreIC::StoreElementHandler(
+               isolate()),
+       IsStoreInArrayLiteralIC());
+ 
+-  if (IsJSProxyMap(*receiver_map)) {
++  if (!IsJSObjectMap(*receiver_map)) {
+     // DefineKeyedOwnIC, which is used to define computed fields in instances,
+-    // should be handled by the slow stub.
+-    if (IsDefineKeyedOwnIC()) {
+-      TRACE_HANDLER_STATS(isolate(), KeyedStoreIC_SlowStub);
+-      return StoreHandler::StoreSlow(isolate(), store_mode);
++    // should handled by the slow stub below instead of the proxy stub.
++    if (IsJSProxyMap(*receiver_map) && !IsDefineKeyedOwnIC()) {
++      return StoreHandler::StoreProxy(isolate());
+     }
+ 
+-    return StoreHandler::StoreProxy(isolate());
++    // Wasm objects or other kind of special objects go through the slow stub.
++    TRACE_HANDLER_STATS(isolate(), KeyedStoreIC_SlowStub);
++    return StoreHandler::StoreSlow(isolate(), store_mode);
+   }
+ 
+   // TODO(ishell): move to StoreHandler::StoreElement().