// Copyright 2025 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package ssa import ( "cmd/compile/internal/types" "testing" ) // ARM64Lt specifies BlockARM64LT func ARM64Lt(cond, sub, alt string) ctrl { return ctrl{BlockARM64LT, cond, []string{sub, alt}} } // ARM64Gt specifies BlockARM64GT func ARM64Gt(cond, sub, alt string) ctrl { return ctrl{BlockARM64GT, cond, []string{sub, alt}} } // ARM64Ne specifies BlockARM64NE func ARM64Ne(cond, sub, alt string) ctrl { return ctrl{BlockARM64NE, cond, []string{sub, alt}} } // ARM64Eq specifies BlockARM64EQ func ARM64Eq(cond, sub, alt string) ctrl { return ctrl{BlockARM64EQ, cond, []string{sub, alt}} } // isNewConditionCorrect verifies that a block has been correctly transformed // to use conditional comparison (CCMP) with the expected parameters. // It checks: // - The block kind is BlockARM64LT (less than condition) // - The control operation is OpARM64CCMPconst (conditional comparison with constant) // - The condition code is OpARM64GreaterThan // - The NZCV flags are set to 1 // - The constant value being compared is 4 // Returns true if all conditions match the expected transformation pattern func isNewConditionCorrect(b *Block) bool { if b.Kind != BlockARM64LT { return false } v := b.Controls[0] if v.Op != OpARM64CCMPconst { return false } params := v.AuxArm64ConditionalParams() if params.Cond() != OpARM64GreaterThan { return false } if params.Nzcv() != 1 { // NZCV flags should be set to 1 for this specific transformation return false } if imm, ok := params.ConstValue(); !ok || imm != 4 { return false } return true } // containsOpARM64CCMP checks if a block contains any ARM64 conditional comparison // operations (CCMP or CCMPconst). This is used in tests to verify that the // if-conversion optimization successfully generated conditional comparison // instructions or to ensure they were not generated when inappropriate. func containsOpARM64CCMP(b *Block) bool { for _, v := range b.Values { if v.Op == OpARM64CCMP || v.Op == OpARM64CCMPconst { return true } } return false } // TestMergeConditionalBranchesWithoutPointers tests the if-conversion optimization // on a simple case of logical AND (cond1 && cond2) without pointer operations. // The test verifies that: // - The optimization correctly transforms nested conditionals into CCMP instructions // - The block structure is properly simplified (inner block becomes plain and empty) // - The resulting control flow uses conditional comparison with correct parameters // - No important blocks are accidentally deleted during transformation // This represents the ideal case where the optimization should apply successfully. func TestMergeConditionalBranchesWithoutPointers(t *testing.T) { t.Run("arm64", func(t *testing.T) { c := testConfigArch(t, "arm64") intType := c.config.Types.Int64 fun := c.Fun("entry", Bloc("entry", Valu("mem", OpInitMem, types.TypeMem, 0, nil, ), Valu("a", OpArg, intType, 0, c.Temp(intType), ), Valu("b", OpArg, intType, 1, c.Temp(intType), ), Valu("cond1", OpARM64CMPconst, types.TypeFlags, 1, nil, "a", ), ARM64Gt("cond1", "second_comparison", "ret_false"), ), Bloc("second_comparison", Valu("cond2", OpARM64CMPconst, types.TypeFlags, 4, nil, "b", ), ARM64Lt("cond2", "ret_false", "ret_true"), ), Bloc("ret_true", Valu("const1", OpARM64MOVDconst, intType, 1, nil, ), Valu("true_result", OpMakeResult, types.TypeMem, 0, nil, "const1", "mem", ), Ret("true_result"), ), Bloc("ret_false", Valu("const0", OpARM64MOVDconst, intType, 0, nil, ), Valu("false_result", OpMakeResult, types.TypeMem, 0, nil, "const0", "mem", ), Ret("false_result"), ), ) CheckFunc(fun.f) mergeConditionalBranches(fun.f) CheckFunc(fun.f) if len(fun.blocks) != 4 { t.Errorf("Important block was deleted") } entryBlock := fun.blocks["entry"] secondBlock := fun.blocks["second_comparison"] if secondBlock.Kind != BlockPlain || len(secondBlock.Values) != 0 { t.Errorf("Block with second condition wasn't cleaned") } if !isNewConditionCorrect(entryBlock) { t.Errorf("Entry block doesn't contain CCMP opertation") } }) } // Test that pointer comparison with memory load doesn't generate CCMP func TestNoCCMPWithPointerAndMemoryLoad(t *testing.T) { t.Run("arm64", func(t *testing.T) { c := testConfigArch(t, "arm64") intType := c.config.Types.Int64 ptrType := c.config.Types.BytePtr fun := c.Fun("entry", Bloc("entry", Valu("mem", OpInitMem, types.TypeMem, 0, nil, ), Valu("ptr", OpArg, ptrType, 0, c.Temp(ptrType), ), Valu("cond1", OpARM64CMPconst, types.TypeFlags, 0, nil, // Compare with nil (0) "ptr", ), ARM64Ne("cond1", "second_comparison", "ret_false"), // ptr != nil ), Bloc("second_comparison", Valu("load", OpLoad, intType, 0, nil, "ptr", "mem", ), Valu("cond2", OpARM64CMPconst, types.TypeFlags, 3, nil, // Compare with 3 "load", ), ARM64Eq("cond2", "ret_true", "ret_false"), // *ptr == 3 ), Bloc("ret_true", Valu("const1", OpARM64MOVDconst, intType, 1, nil, ), Valu("true_result", OpMakeResult, types.TypeMem, 0, nil, "const1", "mem", ), Ret("true_result"), ), Bloc("ret_false", Valu("const0", OpARM64MOVDconst, intType, 0, nil, ), Valu("false_result", OpMakeResult, types.TypeMem, 0, nil, "const0", "mem", ), Ret("false_result"), ), ) CheckFunc(fun.f) mergeConditionalBranches(fun.f) CheckFunc(fun.f) // Verify that the second_comparison block still exists (not optimized away) if fun.blocks["second_comparison"] == nil { t.Errorf("Second comparison block was incorrectly removed") } entryBlock := fun.blocks["entry"] secondBlock := fun.blocks["second_comparison"] // Verify that entry block doesn't contain CCMP operation if containsOpARM64CCMP(entryBlock) { t.Errorf("Entry block contains CCMP operation, but shouldn't due to memory load") } // Verify that second block contains the load operation hasLoad := false for _, v := range secondBlock.Values { if v.Op == OpLoad { hasLoad = true break } } if !hasLoad { t.Errorf("Second comparison block should contain load operation") } // The optimization shouldn't merge these blocks because of the memory operation if secondBlock.Kind == BlockPlain { t.Errorf("Block with memory load was incorrectly cleaned") } }) }