From f737620179b509907e1ca98cab59dbd747ff32d5 Mon Sep 17 00:00:00 2001 From: "Jiang, Zhiwei" Date: Mon, 26 May 2025 13:18:53 +0800 Subject: [PATCH 01/53] [SYCLomatic] gitdiff2yaml Signed-off-by: Jiang, Zhiwei --- clang/tools/dpct/gitdiff2yaml.cpp | 309 ++++++++++++++++++++++++++++++ 1 file changed, 309 insertions(+) create mode 100644 clang/tools/dpct/gitdiff2yaml.cpp diff --git a/clang/tools/dpct/gitdiff2yaml.cpp b/clang/tools/dpct/gitdiff2yaml.cpp new file mode 100644 index 000000000000..d7c6c1eca9ca --- /dev/null +++ b/clang/tools/dpct/gitdiff2yaml.cpp @@ -0,0 +1,309 @@ +//===--- gitdiff2yaml.cpp -------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// Usage: +// $ g++ gitdiff2yaml.cpp -o gitdiff2yaml +// $ cd /path/to/your/git/repo +// $ ./gitdiff2yaml +// This will output the clang replacements in YAML format. +// Limitation: +// (1) The workspace and the staging area should be clean before running +// this tool. +// (2) The line ending in the file should be '\n'. +//===----------------------------------------------------------------------===// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace { + +std::string execGitCommand(const std::string &CMD) { + std::array Buffer; + std::unique_ptr Pipe(popen(CMD.c_str(), "r"), pclose); + if (!Pipe) { + throw std::runtime_error("popen() failed!"); + } + + std::string Result; + while (fgets(Buffer.data(), Buffer.size(), Pipe.get()) != nullptr) { + Result += Buffer.data(); + } + return Result; +} + +struct Replacement { + std::string NewFilePath; + std::string OldFilePath; + unsigned Offset = 0; + unsigned Length = 0; + std::string ReplacementText; +}; + +struct HunkContext { + unsigned OldCurrentLine = 0; + bool InHunk = false; + bool FastForward = false; + std::string CurrentNewFilePath; + std::string CurrentOldFilePath; +}; + +bool startsWith(const std::string &Str, const std::string &Prefix) { + return Str.size() >= Prefix.size() && + Str.compare(0, Prefix.size(), Prefix) == 0; +} + +bool parseHunkHeader(const std::string &Line, HunkContext &HC) { + const std::string HunkHeaderPrefix = "@@ -"; + if (!startsWith(Line, HunkHeaderPrefix)) + return false; + + // E.g., + // @@ -0,0 +1,3 @@ + // ^ + // |-- OldEnd + size_t OldEnd = Line.find(' ', HunkHeaderPrefix.size()); + std::string OldPart = + Line.substr(HunkHeaderPrefix.size(), OldEnd - HunkHeaderPrefix.size()); + size_t Comma = OldPart.find(','); + if (Comma == std::string::npos) { + throw std::runtime_error("Invalid hunk header format: " + Line); + } + HC.OldCurrentLine = std::stoi(OldPart.substr(0, Comma)); + return true; +} + +// vec[0] is -1, which is a placeholder. +// vec[i] /*i is the line number (1-based)*/ is the offset at the line +// beginning. +// Assumption: the line ending in the file is '\n'. +std::vector calculateOldOffset(const std::string &OldFileContent) { + std::vector Ret; + Ret.push_back(-1); // Placeholder for Ret[0]. + std::istringstream OldFileStream(OldFileContent); + std::string Line; + unsigned Offset = 0; + + while (std::getline(OldFileStream, Line)) { + Ret.push_back(Offset); + Offset += Line.size() + + 1; // std::getline does not include the newline character. And we + // assume the line ending is '\n'. So add 1 here. + } + + return Ret; +} + +// 1. Assume the line ending in the file is '\n'. +// 2. The pair (---, +++) may occurs multiple times in one hunk, so we use a +// variable to save the delete (-) operation. The continuous delete operations +// are treated as one operation. +// 3. After the delete operation, if the next line is one or more '+' +// operations, we make them as a replace-replacement. If the next line is a +// context line, the delete operation is a delete-replacement. Then clear the +// variable. +// 4. If we meet insertions ('+') when the variable is empty, we treat it as an +// insert-replacement. +void processHunkBody(const std::string &Line, HunkContext &Ctx, + std::vector &Repls, + const std::vector &CurrentOldFileOffset) { + static std::optional< + std::pair> + DeleteInfo; + static std::optional< + std::pair> + AddInfo; + + auto addRepl = [&]() { + Replacement R; + if (DeleteInfo.has_value() && AddInfo.has_value()) { + // replace-replacement + R.OldFilePath = Ctx.CurrentOldFilePath; + R.NewFilePath = Ctx.CurrentNewFilePath; + R.Length = DeleteInfo->second; + R.Offset = CurrentOldFileOffset[DeleteInfo->first]; + R.ReplacementText = AddInfo->second; + DeleteInfo.reset(); + AddInfo.reset(); + } else if (DeleteInfo.has_value()) { + // delete-replacement + R.OldFilePath = Ctx.CurrentOldFilePath; + R.NewFilePath = Ctx.CurrentNewFilePath; + R.Length = DeleteInfo->second; + R.Offset = CurrentOldFileOffset[DeleteInfo->first]; + R.ReplacementText = ""; + DeleteInfo.reset(); + } else if (AddInfo.has_value()) { + // insert-replacement + R.OldFilePath = Ctx.CurrentOldFilePath; + R.NewFilePath = Ctx.CurrentNewFilePath; + R.Length = AddInfo->second.length(); + R.Offset = CurrentOldFileOffset[AddInfo->first]; + R.ReplacementText = AddInfo->second; + AddInfo.reset(); + } + Repls.push_back(R); + }; + + // Hunk end + if (Line.empty()) { + addRepl(); + Ctx.InHunk = false; + return; + } + + switch (Line[0]) { + case ' ': { + addRepl(); + Ctx.OldCurrentLine++; + break; + } + case '-': { + if (!DeleteInfo.has_value()) { + auto Item = std::pair( + Ctx.OldCurrentLine, + Line.length()); // +1 for the newline character, -1 for the + // '-' in the line beginng + DeleteInfo = Item; + } else { + DeleteInfo->second += + (Line.length()); // +1 for the newline character, -1 for the + // '-' in the line beginng + } + Ctx.OldCurrentLine++; + break; + } + case '+': { + if (!AddInfo.has_value()) { + auto Item = std::pair(Ctx.OldCurrentLine, + Line.substr(1) + '\n'); + AddInfo = Item; + } else { + AddInfo->second += (Line.substr(1) + '\n'); + } + break; + } + } +} + +std::vector parseDiff(const std::string &diffOutput, + const std::string &RepoRoot) { + std::vector replacements; + std::istringstream iss(diffOutput); + std::string line; + + HunkContext HC; + std::vector CurrentOldFileOffset; + + while (std::getline(iss, line)) { + if (startsWith(line, "diff --git")) { + HC.FastForward = false; + continue; + } + if (HC.FastForward) + continue; + + if (startsWith(line, "---")) { + HC.CurrentOldFilePath = + line.substr(4) == "/dev/null" ? "/dev/null" : line.substr(6); + std::ifstream FileStream(RepoRoot + "/" + HC.CurrentOldFilePath); + if (!FileStream.is_open()) { + throw std::runtime_error("Failed to open file: " + RepoRoot + "/" + + HC.CurrentOldFilePath); + } + std::stringstream Buffer; + Buffer << FileStream.rdbuf(); + CurrentOldFileOffset = calculateOldOffset(Buffer.str()); + continue; + } + if (startsWith(line, "+++")) { + HC.CurrentNewFilePath = + line.substr(4) == "/dev/null" ? "/dev/null" : line.substr(6); + if (HC.CurrentOldFilePath == "/dev/null" || + HC.CurrentNewFilePath == "/dev/null") { + HC.FastForward = true; + Replacement R; + R.OldFilePath = HC.CurrentOldFilePath; + R.NewFilePath = HC.CurrentNewFilePath; + replacements.emplace_back(R); + } + continue; + } + + if (parseHunkHeader(line, HC)) { + // Hunk start + HC.InHunk = true; + continue; + } + + if (HC.InHunk) { + processHunkBody(line, HC, replacements, CurrentOldFileOffset); + continue; + } + } + + return replacements; +} + +void printYaml(const std::vector &Repls) { + std::cout << "---" << std::endl; + std::cout << "Replacements:" << std::endl; + for (const auto &R : Repls) { + std::cout << " - FilePath: " << "'" << R.OldFilePath << "'" + << std::endl; + std::cout << " Offset: " << R.Offset << std::endl; + std::cout << " Length: " << R.Length << std::endl; + std::cout << " ReplacementText:" << "\"" << R.ReplacementText << "\"" + << std::endl; + std::cout << " - NewFilePath: " << "'" << R.NewFilePath << "'" + << std::endl; + } + std::cout << "..." << std::endl; +} + +} // namespace + +int main(int argc, char *argv[]) { + if (argc != 2) { + std::cerr << "Usage: gitdiff2yaml " << std::endl; + return 1; + } + std::string OldCommitID = argv[1]; + + std::string RepoRoot = execGitCommand("git rev-parse --show-toplevel"); + RepoRoot = RepoRoot.substr(0, RepoRoot.size() - 1); // Remove the last '\n' + + std::string NewCommitID = execGitCommand("git log -1 --format=\"%H\""); + std::string DiffOutput = execGitCommand("git diff " + OldCommitID); + + execGitCommand("git reset --hard " + OldCommitID); + std::vector Repls = parseDiff(DiffOutput, RepoRoot); + + // Erase emtpy replacements + Repls.erase(std::remove_if(Repls.begin(), Repls.end(), + [](Replacement x) { + return (x.NewFilePath == "" && + x.OldFilePath == "" && x.Offset == 0 && + x.Length == 0 && + x.ReplacementText == ""); + }), + Repls.end()); + + printYaml(Repls); + execGitCommand("git reset --hard " + NewCommitID); + + return 0; +} From 900b2ab496088d2e384b8d9ef594515617844430 Mon Sep 17 00:00:00 2001 From: "Jiang, Zhiwei" Date: Mon, 26 May 2025 14:39:12 +0800 Subject: [PATCH 02/53] Add tests Signed-off-by: Jiang, Zhiwei --- clang/tools/dpct/gitdiff2yaml/1.ref.txt | 13 +++ clang/tools/dpct/gitdiff2yaml/2.ref.txt | 8 ++ clang/tools/dpct/gitdiff2yaml/3.ref.txt | 8 ++ clang/tools/dpct/gitdiff2yaml/4.ref.txt | 33 ++++++ .../dpct/{ => gitdiff2yaml}/gitdiff2yaml.cpp | 75 ++++++++----- clang/tools/dpct/gitdiff2yaml/test.py | 101 ++++++++++++++++++ clang/tools/dpct/gitdiff2yaml/tests.zip | Bin 0 -> 92968 bytes 7 files changed, 213 insertions(+), 25 deletions(-) create mode 100644 clang/tools/dpct/gitdiff2yaml/1.ref.txt create mode 100644 clang/tools/dpct/gitdiff2yaml/2.ref.txt create mode 100644 clang/tools/dpct/gitdiff2yaml/3.ref.txt create mode 100644 clang/tools/dpct/gitdiff2yaml/4.ref.txt rename clang/tools/dpct/{ => gitdiff2yaml}/gitdiff2yaml.cpp (83%) create mode 100644 clang/tools/dpct/gitdiff2yaml/test.py create mode 100644 clang/tools/dpct/gitdiff2yaml/tests.zip diff --git a/clang/tools/dpct/gitdiff2yaml/1.ref.txt b/clang/tools/dpct/gitdiff2yaml/1.ref.txt new file mode 100644 index 000000000000..e288c64de91c --- /dev/null +++ b/clang/tools/dpct/gitdiff2yaml/1.ref.txt @@ -0,0 +1,13 @@ +--- +Replacements: + - FilePath: '/dev/null' + Offset: 0 + Length: 0 + ReplacementText:"" + NewFilePath: 'b.txt' + - FilePath: '/dev/null' + Offset: 0 + Length: 0 + ReplacementText:"" + NewFilePath: 'c.txt' +... diff --git a/clang/tools/dpct/gitdiff2yaml/2.ref.txt b/clang/tools/dpct/gitdiff2yaml/2.ref.txt new file mode 100644 index 000000000000..20d377d8c0ca --- /dev/null +++ b/clang/tools/dpct/gitdiff2yaml/2.ref.txt @@ -0,0 +1,8 @@ +--- +Replacements: + - FilePath: 'b.txt' + Offset: 0 + Length: 0 + ReplacementText:"" + NewFilePath: '/dev/null' +... diff --git a/clang/tools/dpct/gitdiff2yaml/3.ref.txt b/clang/tools/dpct/gitdiff2yaml/3.ref.txt new file mode 100644 index 000000000000..45125ba6d818 --- /dev/null +++ b/clang/tools/dpct/gitdiff2yaml/3.ref.txt @@ -0,0 +1,8 @@ +--- +Replacements: + - FilePath: 'b.txt' + Offset: 12 + Length: 4 + ReplacementText:"777\n" + NewFilePath: 'c.txt' +... diff --git a/clang/tools/dpct/gitdiff2yaml/4.ref.txt b/clang/tools/dpct/gitdiff2yaml/4.ref.txt new file mode 100644 index 000000000000..7c53080ae731 --- /dev/null +++ b/clang/tools/dpct/gitdiff2yaml/4.ref.txt @@ -0,0 +1,33 @@ +--- +Replacements: + - FilePath: 'a.txt' + Offset: 8 + Length: 8 + ReplacementText:"111\n222\n" + NewFilePath: 'a.txt' + - FilePath: 'a.txt' + Offset: 20 + Length: 4 + ReplacementText:"333\n" + NewFilePath: 'a.txt' + - FilePath: 'a.txt' + Offset: 56 + Length: 0 + ReplacementText:"444\n" + NewFilePath: 'a.txt' + - FilePath: 'a.txt' + Offset: 92 + Length: 4 + ReplacementText:"" + NewFilePath: 'a.txt' + - FilePath: 'b.txt' + Offset: 40 + Length: 12 + ReplacementText:"" + NewFilePath: 'b.txt' + - FilePath: 'b.txt' + Offset: 92 + Length: 0 + ReplacementText:"111\n222\n333\n" + NewFilePath: 'b.txt' +... diff --git a/clang/tools/dpct/gitdiff2yaml.cpp b/clang/tools/dpct/gitdiff2yaml/gitdiff2yaml.cpp similarity index 83% rename from clang/tools/dpct/gitdiff2yaml.cpp rename to clang/tools/dpct/gitdiff2yaml/gitdiff2yaml.cpp index d7c6c1eca9ca..c6501ae1d71d 100644 --- a/clang/tools/dpct/gitdiff2yaml.cpp +++ b/clang/tools/dpct/gitdiff2yaml/gitdiff2yaml.cpp @@ -31,6 +31,8 @@ namespace { +const std::string LineEnd = "\\n"; + std::string execGitCommand(const std::string &CMD) { std::array Buffer; std::unique_ptr Pipe(popen(CMD.c_str(), "r"), pclose); @@ -150,7 +152,7 @@ void processHunkBody(const std::string &Line, HunkContext &Ctx, // insert-replacement R.OldFilePath = Ctx.CurrentOldFilePath; R.NewFilePath = Ctx.CurrentNewFilePath; - R.Length = AddInfo->second.length(); + R.Length = 0; R.Offset = CurrentOldFileOffset[AddInfo->first]; R.ReplacementText = AddInfo->second; AddInfo.reset(); @@ -189,10 +191,10 @@ void processHunkBody(const std::string &Line, HunkContext &Ctx, case '+': { if (!AddInfo.has_value()) { auto Item = std::pair(Ctx.OldCurrentLine, - Line.substr(1) + '\n'); + Line.substr(1) + LineEnd); AddInfo = Item; } else { - AddInfo->second += (Line.substr(1) + '\n'); + AddInfo->second += (Line.substr(1) + LineEnd); } break; } @@ -219,14 +221,16 @@ std::vector parseDiff(const std::string &diffOutput, if (startsWith(line, "---")) { HC.CurrentOldFilePath = line.substr(4) == "/dev/null" ? "/dev/null" : line.substr(6); - std::ifstream FileStream(RepoRoot + "/" + HC.CurrentOldFilePath); - if (!FileStream.is_open()) { - throw std::runtime_error("Failed to open file: " + RepoRoot + "/" + - HC.CurrentOldFilePath); + if (HC.CurrentOldFilePath != "/dev/null") { + std::ifstream FileStream(RepoRoot + "/" + HC.CurrentOldFilePath); + if (!FileStream.is_open()) { + throw std::runtime_error("Failed to open file: " + RepoRoot + "/" + + HC.CurrentOldFilePath); + } + std::stringstream Buffer; + Buffer << FileStream.rdbuf(); + CurrentOldFileOffset = calculateOldOffset(Buffer.str()); } - std::stringstream Buffer; - Buffer << FileStream.rdbuf(); - CurrentOldFileOffset = calculateOldOffset(Buffer.str()); continue; } if (startsWith(line, "+++")) { @@ -258,29 +262,39 @@ std::vector parseDiff(const std::string &diffOutput, return replacements; } -void printYaml(const std::vector &Repls) { - std::cout << "---" << std::endl; - std::cout << "Replacements:" << std::endl; +void printYaml(std::ostream &stream, const std::vector &Repls) { + stream << "---" << std::endl; + stream << "Replacements:" << std::endl; for (const auto &R : Repls) { - std::cout << " - FilePath: " << "'" << R.OldFilePath << "'" - << std::endl; - std::cout << " Offset: " << R.Offset << std::endl; - std::cout << " Length: " << R.Length << std::endl; - std::cout << " ReplacementText:" << "\"" << R.ReplacementText << "\"" - << std::endl; - std::cout << " - NewFilePath: " << "'" << R.NewFilePath << "'" - << std::endl; + stream << " - FilePath: " << "'" << R.OldFilePath << "'" + << std::endl; + stream << " Offset: " << R.Offset << std::endl; + stream << " Length: " << R.Length << std::endl; + stream << " ReplacementText:" << "\"" << R.ReplacementText << "\"" + << std::endl; + stream << " NewFilePath: " << "'" << R.NewFilePath << "'" + << std::endl; } - std::cout << "..." << std::endl; + stream << "..." << std::endl; } } // namespace int main(int argc, char *argv[]) { - if (argc != 2) { - std::cerr << "Usage: gitdiff2yaml " << std::endl; + if (argc != 2 && argc != 4) { + std::cerr << "Usage: gitdiff2yaml [-o outputfile]" + << std::endl; return 1; } + bool OutputToFile = false; + if (argc == 4) { + if (std::string(argv[2]) != "-o") { + std::cerr << "Invalid option: " << argv[2] << std::endl; + return 1; + } + OutputToFile = true; + } + std::string OldCommitID = argv[1]; std::string RepoRoot = execGitCommand("git rev-parse --show-toplevel"); @@ -302,7 +316,18 @@ int main(int argc, char *argv[]) { }), Repls.end()); - printYaml(Repls); + if (OutputToFile) { + std::ofstream OutFile(argv[3]); + if (!OutFile.is_open()) { + std::cerr << "Failed to open output file: " << argv[3] << std::endl; + return 1; + } + printYaml(OutFile, Repls); + OutFile.close(); + } else { + printYaml(std::cout, Repls); + } + execGitCommand("git reset --hard " + NewCommitID); return 0; diff --git a/clang/tools/dpct/gitdiff2yaml/test.py b/clang/tools/dpct/gitdiff2yaml/test.py new file mode 100644 index 000000000000..7c4dc291cb5b --- /dev/null +++ b/clang/tools/dpct/gitdiff2yaml/test.py @@ -0,0 +1,101 @@ +# ===----- test.py --------------------------------------------------------=== # +# +# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +# See https://llvm.org/LICENSE.txt for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# +# ===----------------------------------------------------------------------=== # + +from pathlib import Path +import os +import shutil +import subprocess + +def run_command(cmd): + try: + result = subprocess.run( + cmd, + check=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True + ) + return result.stdout + except subprocess.CalledProcessError as e: + print(f"Command fail: {e.stderr}") + return None + +def compare_with_diff(ref_file): + result = subprocess.run( + ["diff", "-u", ref_file, "temp.txt"], + capture_output=True, + text=True + ) + + if result.returncode == 0: + return True + else: + print("Found difference:\n" + result.stdout) + return False + +# Clean up previous test directory if it exists +test_dir = Path("tests") +if test_dir.exists() and test_dir.is_dir(): + shutil.rmtree(test_dir) + +# Check if gitdiff2yaml exists +gitdiff2yaml_path = Path("gitdiff2yaml") +if not gitdiff2yaml_path.exists(): + print("gitdiff2yaml does not exist. Please build it first.") + exit(1) + +run_command(["unzip", "tests.zip"]) +os.chdir("tests") + +# Test 1 +os.chdir("1") +old_commit_id = run_command(["git", "rev-parse", "HEAD~1"]).strip() +result = run_command(["../../gitdiff2yaml", old_commit_id, "-o", "temp.txt"]) +test1_res = compare_with_diff("../../1.ref.txt") +if test1_res: + print("Test 1 passed") +else: + print("Test 1 failed") +os.chdir("..") + +# Test 2 +os.chdir("2") +old_commit_id = run_command(["git", "rev-parse", "HEAD~1"]).strip() +result = run_command(["../../gitdiff2yaml", old_commit_id, "-o", "temp.txt"]) +test1_res = compare_with_diff("../../2.ref.txt") +if test1_res: + print("Test 2 passed") +else: + print("Test 2 failed") +os.chdir("..") + +# Test 3 +os.chdir("3") +old_commit_id = run_command(["git", "rev-parse", "HEAD~1"]).strip() +result = run_command(["../../gitdiff2yaml", old_commit_id, "-o", "temp.txt"]) +test1_res = compare_with_diff("../../3.ref.txt") +if test1_res: + print("Test 3 passed") +else: + print("Test 3 failed") +os.chdir("..") + +# Test 4 +os.chdir("4") +old_commit_id = run_command(["git", "rev-parse", "HEAD~1"]).strip() +result = run_command(["../../gitdiff2yaml", old_commit_id, "-o", "temp.txt"]) +test1_res = compare_with_diff("../../4.ref.txt") +if test1_res: + print("Test 4 passed") +else: + print("Test 4 failed") +os.chdir("..") + +# Clean up +os.chdir("..") +shutil.rmtree(test_dir) diff --git a/clang/tools/dpct/gitdiff2yaml/tests.zip b/clang/tools/dpct/gitdiff2yaml/tests.zip new file mode 100644 index 0000000000000000000000000000000000000000..dec688f804e831f90939d1b192432d2f6d40da42 GIT binary patch literal 92968 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%0A%B0wk~k z-jE1R)*ALGP3=s{A!1hAj1Z_!H!ELZs29Vmw8?=z&+qR-X`$(K*v#|KnX_OVObw#rxvvDH!@#ae)xmBrLX2TV4u?O+O<6``AzH;KAdv$B|Jqh{sJZdgeyW+heF%$sgj zrgbL@lg&y8k?3Zn*qa!MZdN*oMmH=bxkQk9=Ryv4BH!H_INsSk6Ryv48H!C*}r$u6zl@21(&C0S*6C<+C zN(T|?X62Nz#E2BL(m^P?Svds!gac{9kp4EqM&U+HG$kI!S#id-S8tZ^%W+nSDY(yX0ee^(t?L8w{z@Kj8e zU{*TWbx591z>d|^7hI8>pkX^bH4O`3F)OK=8wJ7L5_D5gZaOAxnw2*G0IrMfB6|{b z%`Iv%#Zj~JUAnksD@hGu_GnDt^(k+IVz&fUM$O7wb|EZgrA^Pmp<@jc+3^`Y>zPUF zfTCeZ)iyeJ2hiSg_k-!MN$`pocZPO>McXthGuTg5@TV(*QqdpOtn4x?lCEgD6!6z8 zv0rpB%u1um&4JpA-glja!}dumMo1;`Bsah>3_msNF-)SmPGx}nAv8Qh)G%aylEqyhGmPoF5PL; zQuv8Fot-#TulCKX$_l6hywE4=r403e;8TK%^%~G z5!m_)0;<>8R(&9Oj5M1|$sVYSULAa>%=0KdEjX1xIZA5POa@SmN`bM`Dq$)dE-G%jpr0lu!IE9E?zzRzz|&}JQm z>eCX2Pu>K^2fWZR@*P7Rd=JSfAwT%ZO)21mKBLTQAx=_4x=#wo;A`PvK$474zHM^A ze}EZnVYOE%739KWK=s+V$_>yI!so(+80z2yVNI6=@cpNz`FK?Vzv#-#d{7hN zt0(twI*3PiS3OIK@uJ;T2T|zm>W?dkQP}RPgNSr@HR%IlM3%eiARgUaotjOJ7wxV( zh(vc+w{N6HVz{dgBGTQ}id%>g+3u=?h;(;#_I6@Kio5C{6y05Y=AYzHWOvm;D7w2^ z^D}BF++B4LhwiS*yNGef?y7@Gba(ZIJ;X?Kca<8=cr1eVSGv1;buSjJ$i|||8n+U- z@hIJ^L7$yq(^0Pk?ymOPhshG$RVTX+s=GRBKNi5^u2M5M3WD#y(M>_+1DLSsu2Ofs zWLM6>n;I3nhbgrbys7-M&5X1A{t)?nV+&o zy{)(DSvYj8fg(FT5EDEe#C<^uR5UE9+D0d20(TY26#YT1&|5{?yz#hLz&@Ec0;tiy z+f2!eWXd--eaj$CZUo`!>>Jux!z1 z)%y>c1wbs($akCh>{>u|`gfaS80y9NZqp_Qh~6n0E#NPxtC##GrG=)e2azz0&Up^g zM=O{`(~o>JNnrh0T|KBy|7P+WLtXR-I6ri~R9)ja@F}6!uX2h2P6dPi0zS8s)S#^M zg=54f2b?Q|wlMQ6N(JSxw@tvcSX&6yOJAz6A`A2lyimvHAVXc^z<0D_$PYdu)B-Xu z0LB6@Ybr1?6ma0%ALIi*Su{K^n;fPv`|#UClom$5nY_*(AyA!uNS{AUS1-mPeVZKc zB?RrmW3(LPuzPQ_ia_ZYU4EhgVXdgcKHKm0>J;eF^ekH(*nyf1RfC9e~6*NH) zNq$igWbipG=vKpZv&n%y50xX793o$Z+ROo{PX8)&4MV*cUxnJ_z+TB7I!bAwX?jps zvdW;(Vm)x2*2~~>!KV`*@TwJa^1M3i>pVCVRR=f58c)K5|4qq zdN%dJiyztJ8QYxD39LS-t5-~Ibik~~wH*&|m^0ngEBOtUbCj!B%(PKgujIFwyv5a{ z3Y$GiclElEg~_g-gGh8&FK~v~1iGu|AR67(TYZ)ojpgb&h(~wzZk#8^i+1%KM4`KS z7k(s0VY_+`BGO&GS_Q<2ELYD#Ji4nl=`uB5w5#VJ65ZAN_$Dn9!_{*Tk?!i#JVyzwnI07}S%RzQ zWYjD=Xo)JMx>$Cr&i9jW=vV_qc6`RrDFvXz$d0(M1B!+vanzIn Date: Tue, 27 May 2025 09:57:13 +0800 Subject: [PATCH 03/53] refine repl Signed-off-by: Jiang, Zhiwei --- .../include/clang/Tooling/Core/Replacement.h | 54 ++++++++----- .../include/clang/Tooling/ReplacementsYaml.h | 81 ++++++++++--------- clang/lib/DPCT/AnalysisInfo.cpp | 2 +- clang/lib/DPCT/AnalysisInfo.h | 7 +- clang/lib/DPCT/FileGenerator/GenFiles.cpp | 8 +- .../DPCT/IncMigration/ExternalReplacement.cpp | 6 +- .../DPCT/IncMigration/ExternalReplacement.h | 4 +- clang/lib/DPCT/RulesLang/RulesLang.h | 6 +- .../RulesLang/RulesLangNoneAPIAndType.cpp | 8 +- clang/lib/DPCT/TextModification.h | 12 +-- 10 files changed, 107 insertions(+), 81 deletions(-) diff --git a/clang/include/clang/Tooling/Core/Replacement.h b/clang/include/clang/Tooling/Core/Replacement.h index 40fb47fb378d..41c2baa5903d 100644 --- a/clang/include/clang/Tooling/Core/Replacement.h +++ b/clang/include/clang/Tooling/Core/Replacement.h @@ -139,9 +139,6 @@ class Replacement { unsigned getOffset() const { return ReplacementRange.getOffset(); } unsigned getLength() const { return ReplacementRange.getLength(); } StringRef getReplacementText() const { return ReplacementText; } -#ifdef SYCLomatic_CUSTOMIZATION - void setReplacementText(const std::string Str) { ReplacementText = Str; } -#endif // SYCLomatic_CUSTOMIZATION /// @} /// Applies the replacement on the Rewriter. @@ -149,8 +146,35 @@ class Replacement { /// Returns a human readable string representation. std::string toString() const; + #ifdef SYCLomatic_CUSTOMIZATION - void setBlockLevelFormatFlag(bool Flag = true) { BlockLevelFormatFlag = Flag; } +protected: +#else +private: +#endif // SYCLomatic_CUSTOMIZATION + void setFromSourceLocation(const SourceManager &Sources, SourceLocation Start, + unsigned Length, StringRef ReplacementText); + void setFromSourceRange(const SourceManager &Sources, + const CharSourceRange &Range, + StringRef ReplacementText, + const LangOptions &LangOpts); + + std::string FilePath; + Range ReplacementRange; + std::string ReplacementText; +}; + +#ifdef SYCLomatic_CUSTOMIZATION +class DpctReplacement : public Replacement { +public: + using Replacement::Replacement; + DpctReplacement(const Replacement &R) : Replacement(R) {} + + void setReplacementText(const std::string Str) { ReplacementText = Str; } + + void setBlockLevelFormatFlag(bool Flag = true) { + BlockLevelFormatFlag = Flag; + } bool getBlockLevelFormatFlag() const { return BlockLevelFormatFlag; } void setNotFormatFlag() { NotFormatFlag = true; } bool getNotFormatFlag() const { return NotFormatFlag; } @@ -165,20 +189,8 @@ class Replacement { void setOffset(unsigned int Offset) { ReplacementRange = Range(Offset, ReplacementRange.getLength()); } -#endif // SYCLomatic_CUSTOMIZATION private: - void setFromSourceLocation(const SourceManager &Sources, SourceLocation Start, - unsigned Length, StringRef ReplacementText); - void setFromSourceRange(const SourceManager &Sources, - const CharSourceRange &Range, - StringRef ReplacementText, - const LangOptions &LangOpts); - - std::string FilePath; - Range ReplacementRange; - std::string ReplacementText; -#ifdef SYCLomatic_CUSTOMIZATION bool BlockLevelFormatFlag = false; bool NotFormatFlag = false; // Record the __constant__ variable is used in host, device or hostdevice @@ -193,8 +205,8 @@ class Replacement { // appending "_host_ct1". Since the original name can only be collected when // it used in device code, so we need record it. std::string NewHostVarName = ""; -#endif // SYCLomatic_CUSTOMIZATION }; +#endif // SYCLomatic_CUSTOMIZATION enum class replacement_error { fail_to_apply = 0, @@ -265,7 +277,11 @@ inline bool operator!=(const Replacement &LHS, const Replacement &RHS) { /// offset (i.e. order-dependent). class Replacements { private: +#ifdef SYCLomatic_CUSTOMIZATION + using ReplacementsImpl = std::set; +#else using ReplacementsImpl = std::set; +#endif // SYCLomatic_CUSTOMIZATION public: using const_iterator = ReplacementsImpl::const_iterator; @@ -446,8 +462,10 @@ struct TranslationUnitReplacements { // Keep here only for backward compatibility - end std::map> CompileTargets; std::map OptionMap; -#endif // SYCLomatic_CUSTOMIZATION + std::vector Replacements; +#else std::vector Replacements; +#endif // SYCLomatic_CUSTOMIZATION }; /// Calculates the new ranges after \p Replaces are applied. These diff --git a/clang/include/clang/Tooling/ReplacementsYaml.h b/clang/include/clang/Tooling/ReplacementsYaml.h index 2e5e9703f8e1..60c370e73098 100644 --- a/clang/include/clang/Tooling/ReplacementsYaml.h +++ b/clang/include/clang/Tooling/ReplacementsYaml.h @@ -31,6 +31,7 @@ LLVM_YAML_IS_STRING_MAP(std::vector) LLVM_YAML_IS_SEQUENCE_VECTOR(clang::tooling::MainSourceFileInfo) LLVM_YAML_IS_STRING_MAP(std::vector) LLVM_YAML_IS_STRING_MAP(clang::tooling::OptionInfo) +LLVM_YAML_IS_SEQUENCE_VECTOR(clang::tooling::DpctReplacement) #endif // SYCLomatic_CUSTOMIZATION namespace llvm { @@ -42,16 +43,43 @@ template <> struct MappingTraits { /// Helper to (de)serialize a Replacement since we don't have direct /// access to its data members. struct NormalizedReplacement { -#ifdef SYCLomatic_CUSTOMIZATION - NormalizedReplacement(const IO &) - : FilePath(""), Offset(0), Length(0), ReplacementText(""), - ConstantFlag(""), ConstantOffset(0), InitStr(""), NewHostVarName(""), - BlockLevelFormatFlag(false) { - } + NormalizedReplacement(const IO &) : Offset(0), Length(0) {} NormalizedReplacement(const IO &, const clang::tooling::Replacement &R) : FilePath(R.getFilePath()), Offset(R.getOffset()), - Length(R.getLength()), ReplacementText(R.getReplacementText()), + Length(R.getLength()), ReplacementText(R.getReplacementText()) {} + + clang::tooling::Replacement denormalize(const IO &) { + return clang::tooling::Replacement(FilePath, Offset, Length, + ReplacementText); + } + + std::string FilePath; + unsigned int Offset; + unsigned int Length; + std::string ReplacementText; + }; + + static void mapping(IO &Io, clang::tooling::Replacement &R) { + MappingNormalization + Keys(Io, R); + Io.mapRequired("FilePath", Keys->FilePath); + Io.mapRequired("Offset", Keys->Offset); + Io.mapRequired("Length", Keys->Length); + Io.mapRequired("ReplacementText", Keys->ReplacementText); + } +}; + +#ifdef SYCLomatic_CUSTOMIZATION +template <> struct MappingTraits { + struct NormalizedDpctReplacement { + NormalizedDpctReplacement(const IO &io) + : Base(io), ConstantFlag(""), ConstantOffset(0), InitStr(""), + NewHostVarName(""), BlockLevelFormatFlag(false) {} + + NormalizedDpctReplacement(const IO &io, + const clang::tooling::DpctReplacement &R) + : Base(io, static_cast(R)), ConstantOffset(R.getConstantOffset()), InitStr(R.getInitStr()), NewHostVarName(R.getNewHostVarName()), BlockLevelFormatFlag(R.getBlockLevelFormatFlag()) { @@ -67,9 +95,8 @@ template <> struct MappingTraits { } } - clang::tooling::Replacement denormalize(const IO &) { - auto R = clang::tooling::Replacement(FilePath, Offset, Length, - ReplacementText); + clang::tooling::DpctReplacement denormalize(const IO &io) { + clang::tooling::DpctReplacement R = Base.denormalize(io); if (ConstantFlag == "HostDeviceConstant") { R.setConstantFlag(clang::dpct::ConstantFlagType::HostDevice); } else if (ConstantFlag == "DeviceConstant") { @@ -85,48 +112,28 @@ template <> struct MappingTraits { R.setBlockLevelFormatFlag(BlockLevelFormatFlag); return R; } -#else - NormalizedReplacement(const IO &) : Offset(0), Length(0) {} - - NormalizedReplacement(const IO &, const clang::tooling::Replacement &R) - : FilePath(R.getFilePath()), Offset(R.getOffset()), - Length(R.getLength()), ReplacementText(R.getReplacementText()) {} - - clang::tooling::Replacement denormalize(const IO &) { - return clang::tooling::Replacement(FilePath, Offset, Length, - ReplacementText); - } -#endif // SYCLomatic_CUSTOMIZATION - std::string FilePath; - unsigned int Offset; - unsigned int Length; - std::string ReplacementText; -#ifdef SYCLomatic_CUSTOMIZATION + MappingTraits::NormalizedReplacement Base; std::string ConstantFlag = ""; unsigned int ConstantOffset = 0; std::string InitStr = ""; std::string NewHostVarName = ""; bool BlockLevelFormatFlag = false; -#endif // SYCLomatic_CUSTOMIZATION }; - static void mapping(IO &Io, clang::tooling::Replacement &R) { - MappingNormalization - Keys(Io, R); - Io.mapRequired("FilePath", Keys->FilePath); - Io.mapRequired("Offset", Keys->Offset); - Io.mapRequired("Length", Keys->Length); - Io.mapRequired("ReplacementText", Keys->ReplacementText); -#ifdef SYCLomatic_CUSTOMIZATION + static void mapping(IO &Io, clang::tooling::DpctReplacement &R) { + MappingNormalization + Keys(Io, R); + MappingTraits::mapping(Io, R); Io.mapOptional("ConstantFlag", Keys->ConstantFlag); Io.mapOptional("ConstantOffset", Keys->ConstantOffset); Io.mapOptional("InitStr", Keys->InitStr); Io.mapOptional("NewHostVarName", Keys->NewHostVarName); Io.mapOptional("BlockLevelFormatFlag", Keys->BlockLevelFormatFlag); -#endif // SYCLomatic_CUSTOMIZATION } }; +#endif // SYCLomatic_CUSTOMIZATION #ifdef SYCLomatic_CUSTOMIZATION template <> struct MappingTraits { diff --git a/clang/lib/DPCT/AnalysisInfo.cpp b/clang/lib/DPCT/AnalysisInfo.cpp index 065f2117e28c..66255172efd8 100644 --- a/clang/lib/DPCT/AnalysisInfo.cpp +++ b/clang/lib/DPCT/AnalysisInfo.cpp @@ -2464,7 +2464,7 @@ std::map DpctGlobalInfo::FunctionCallInMacroMigrateRecord; std::map DpctGlobalInfo::EndOfEmptyMacros; std::map DpctGlobalInfo::BeginOfEmptyMacros; -std::unordered_map> +std::unordered_map> DpctGlobalInfo::FileRelpsMap; std::unordered_map DpctGlobalInfo::MsfInfoMap; diff --git a/clang/lib/DPCT/AnalysisInfo.h b/clang/lib/DPCT/AnalysisInfo.h index 4eedfb910a25..68b1075c55d6 100644 --- a/clang/lib/DPCT/AnalysisInfo.h +++ b/clang/lib/DPCT/AnalysisInfo.h @@ -37,6 +37,7 @@ #include "clang/Format/Format.h" #include "clang/Frontend/CompilerInstance.h" +#include "clang/Tooling/Core/Replacement.h" #include "clang/Tooling/Core/UnifiedPath.h" llvm::StringRef getReplacedName(const clang::NamedDecl *D); @@ -507,7 +508,7 @@ class DpctFileInfo { getConstantMacroTMSet() { return ConstantMacroTMSet; } - std::vector &getReplacements() { + std::vector &getReplacements() { return PreviousTUReplFromYAML->Replacements; } std::unordered_map> & @@ -1258,7 +1259,7 @@ class DpctGlobalInfo { return FileSetInCompilationDB; } static std::unordered_map> & + std::vector> & getFileRelpsMap() { return FileRelpsMap; } @@ -1643,7 +1644,7 @@ class DpctGlobalInfo { // value: The end location of the macro expansion static std::map BeginOfEmptyMacros; static std::unordered_map> + std::vector> FileRelpsMap; static std::unordered_map MsfInfoMap; diff --git a/clang/lib/DPCT/FileGenerator/GenFiles.cpp b/clang/lib/DPCT/FileGenerator/GenFiles.cpp index a2d95abc735b..69357d6632cc 100644 --- a/clang/lib/DPCT/FileGenerator/GenFiles.cpp +++ b/clang/lib/DPCT/FileGenerator/GenFiles.cpp @@ -442,7 +442,7 @@ void processAllFiles(StringRef InRoot, StringRef OutRoot, static void getMainSrcFilesRepls( - std::vector &MainSrcFilesRepls) { + std::vector &MainSrcFilesRepls) { auto &FileRelpsMap = DpctGlobalInfo::getFileRelpsMap(); for (const auto &Entry : FileRelpsMap) for (const auto &Repl : Entry.second) @@ -456,7 +456,7 @@ static void getMainSrcFilesInfo( } static void saveUpdatedMigrationDataIntoYAML( - std::vector &MainSrcFilesRepls, + std::vector &MainSrcFilesRepls, std::vector &MainSrcFilesInfo, clang::tooling::UnifiedPath YamlFile, clang::tooling::UnifiedPath SrcFile, std::unordered_map &MainSrcFileMap) { @@ -512,7 +512,7 @@ int writeReplacementsToFiles( clang::tooling::UnifiedPath &InRoot, std::vector &MainSrcFilesInfo, std::unordered_map &MainSrcFileMap, - std::vector &MainSrcFilesRepls, + std::vector &MainSrcFilesRepls, std::unordered_map> &FileRangesMap, std::unordered_map MainSrcFilesRepls; + std::vector MainSrcFilesRepls; std::vector MainSrcFilesInfo; if (ReplSYCL.empty()) { diff --git a/clang/lib/DPCT/IncMigration/ExternalReplacement.cpp b/clang/lib/DPCT/IncMigration/ExternalReplacement.cpp index 6ee889b28bad..c2203dbef1ca 100644 --- a/clang/lib/DPCT/IncMigration/ExternalReplacement.cpp +++ b/clang/lib/DPCT/IncMigration/ExternalReplacement.cpp @@ -40,7 +40,7 @@ namespace dpct { int save2Yaml( clang::tooling::UnifiedPath &YamlFile, clang::tooling::UnifiedPath &SrcFileName, - const std::vector &Replaces, + const std::vector &Replaces, const std::vector &MainSrcFilesDigest, const std::map> @@ -109,7 +109,7 @@ int loadFromYaml(const clang::tooling::UnifiedPath &Input, void mergeAndUniqueReps( Replacements &Replaces, - const std::vector &PreRepls) { + const std::vector &PreRepls) { bool DupFlag = false; for (const auto &OldR : PreRepls) { @@ -158,7 +158,7 @@ int mergeExternalReps(clang::tooling::UnifiedPath InRootSrcFilePath, llvm::errs() << "Saved new version of " << YamlFile << " file\n"; - std::vector Repls(Replaces.begin(), + std::vector Repls(Replaces.begin(), Replaces.end()); // For header file, its hash content digest and HasCUDASytax field is not diff --git a/clang/lib/DPCT/IncMigration/ExternalReplacement.h b/clang/lib/DPCT/IncMigration/ExternalReplacement.h index 20e819d16fc1..3a7795c60ab1 100644 --- a/clang/lib/DPCT/IncMigration/ExternalReplacement.h +++ b/clang/lib/DPCT/IncMigration/ExternalReplacement.h @@ -36,7 +36,7 @@ int loadFromYaml(const clang::tooling::UnifiedPath &Input, int save2Yaml( clang::tooling::UnifiedPath &YamlFile, clang::tooling::UnifiedPath &SrcFileName, - const std::vector &Replaces, + const std::vector &Replaces, const std::vector &MainSrcFilesDigest, const std::map> @@ -44,7 +44,7 @@ int save2Yaml( void mergeAndUniqueReps( clang::tooling::Replacements &Replaces, - const std::vector &PreRepls); + const std::vector &PreRepls); } // namespace dpct } // namespace clang diff --git a/clang/lib/DPCT/RulesLang/RulesLang.h b/clang/lib/DPCT/RulesLang/RulesLang.h index b3493eadabd2..09665dc5bdaa 100644 --- a/clang/lib/DPCT/RulesLang/RulesLang.h +++ b/clang/lib/DPCT/RulesLang/RulesLang.h @@ -500,9 +500,9 @@ class ConstantMemVarMigrationRule : public NamedMigrationRule Info); bool currentIsHost(const VarDecl *VD, std::string VarName); }; diff --git a/clang/lib/DPCT/RulesLang/RulesLangNoneAPIAndType.cpp b/clang/lib/DPCT/RulesLang/RulesLangNoneAPIAndType.cpp index bcc2e0c5e117..f2f56fe4907c 100644 --- a/clang/lib/DPCT/RulesLang/RulesLangNoneAPIAndType.cpp +++ b/clang/lib/DPCT/RulesLang/RulesLangNoneAPIAndType.cpp @@ -319,7 +319,7 @@ void ConstantMemVarMigrationRule::runRule( } void ConstantMemVarMigrationRule::previousHCurrentD(const VarDecl *VD, - tooling::Replacement &R) { + tooling::DpctReplacement &R) { // 1. emit DPCT1055 warning // 2. add a new variable for host // 3. insert dpct::constant_memory and add the info from that replacement @@ -368,11 +368,11 @@ void ConstantMemVarMigrationRule::previousHCurrentD(const VarDecl *VD, emplaceTransformation(DeviceRepl); } - R = tooling::Replacement(R.getFilePath(), 0, 0, ""); + R = tooling::DpctReplacement(R.getFilePath(), 0, 0, ""); } void ConstantMemVarMigrationRule::previousDCurrentH(const VarDecl *VD, - tooling::Replacement &R) { + tooling::DpctReplacement &R) { // 1. change DeviceConstant to HostDeviceConstant // 2. emit DPCT1055 warning (warning info is from previous device case) // 3. add a new variable for host (decl info is from previous device case) @@ -401,7 +401,7 @@ void ConstantMemVarMigrationRule::previousDCurrentH(const VarDecl *VD, emplaceTransformation(new InsertText(SL, std::move(NewDecl))); } -void ConstantMemVarMigrationRule::removeHostConstantWarning(Replacement &R) { +void ConstantMemVarMigrationRule::removeHostConstantWarning(DpctReplacement &R) { std::string ReplStr = R.getReplacementText().str(); // warning text of Diagnostics::HOST_CONSTANT diff --git a/clang/lib/DPCT/TextModification.h b/clang/lib/DPCT/TextModification.h index 7636db4fbddd..3192c9468908 100644 --- a/clang/lib/DPCT/TextModification.h +++ b/clang/lib/DPCT/TextModification.h @@ -38,10 +38,10 @@ enum ReplacementType { RT_ForSYCLMigration = 0, RT_CUDAWithCodePin }; /// AST Rule. Further Analysis Pass like Merge Pass can happen based /// on this meta info of Replacement. /// eg. Replacement happen at same position may be merged to avoid conflict. -class ExtReplacement : public tooling::Replacement { +class ExtReplacement : public tooling::DpctReplacement { public: /// Creates an invalid (not applicable) replacement. - ExtReplacement() : Replacement(){}; + ExtReplacement() : DpctReplacement(){}; /// Creates a replacement of the range [Offset, Offset+Length) in /// FilePath with ReplacementText. @@ -51,27 +51,27 @@ class ExtReplacement : public tooling::Replacement { /// \param Length The length of the range in bytes. ExtReplacement(clang::tooling::UnifiedPath FilePath, unsigned Offset, unsigned Length, StringRef ReplacementText, const TextModification *_TM) - : Replacement(FilePath.getCanonicalPath(), Offset, Length, ReplacementText), TM(_TM) {} + : DpctReplacement(FilePath.getCanonicalPath(), Offset, Length, ReplacementText), TM(_TM) {} /// Creates a Replacement of the range [Start, Start+Length) with /// ReplacementText. ExtReplacement(const SourceManager &Sources, SourceLocation Start, unsigned Length, StringRef ReplacementText, const TextModification *_TM) - : Replacement(Sources, Start, Length, ReplacementText), TM(_TM) {} + : DpctReplacement(Sources, Start, Length, ReplacementText), TM(_TM) {} /// Creates a Replacement of the given range with ReplacementText. ExtReplacement(const SourceManager &Sources, const CharSourceRange &Range, StringRef ReplacementText, const TextModification *_TM, const LangOptions &LangOpts = LangOptions()) - : Replacement(Sources, Range, ReplacementText, LangOpts), TM(_TM) {} + : DpctReplacement(Sources, Range, ReplacementText, LangOpts), TM(_TM) {} /// Creates a Replacement of the node with ReplacementText. template ExtReplacement(const SourceManager &Sources, const Node &NodeToReplace, StringRef ReplacementText, const TextModification *_TM, const LangOptions &LangOpts = LangOptions()) - : Replacement(Sources, NodeToReplace, ReplacementText, LangOpts), + : DpctReplacement(Sources, NodeToReplace, ReplacementText, LangOpts), TM(_TM) {} void setInsertPosition(InsertPosition IP) { InsertPos = IP; } unsigned int getInsertPosition() const { return InsertPos; } From 59227ff7b25ad6161cfbec6d5adc5a34a9e552ec Mon Sep 17 00:00:00 2001 From: "Jiang, Zhiwei" Date: Tue, 27 May 2025 10:02:54 +0800 Subject: [PATCH 04/53] format Signed-off-by: Jiang, Zhiwei --- clang/include/clang/Tooling/ReplacementsYaml.h | 2 +- clang/lib/DPCT/FileGenerator/GenFiles.cpp | 1 - clang/lib/DPCT/IncMigration/ExternalReplacement.cpp | 2 +- clang/lib/DPCT/RulesLang/RulesLangNoneAPIAndType.cpp | 11 ++++++----- clang/lib/DPCT/TextModification.h | 11 +++++++---- 5 files changed, 15 insertions(+), 12 deletions(-) diff --git a/clang/include/clang/Tooling/ReplacementsYaml.h b/clang/include/clang/Tooling/ReplacementsYaml.h index 60c370e73098..ab2add9f6c42 100644 --- a/clang/include/clang/Tooling/ReplacementsYaml.h +++ b/clang/include/clang/Tooling/ReplacementsYaml.h @@ -62,7 +62,7 @@ template <> struct MappingTraits { static void mapping(IO &Io, clang::tooling::Replacement &R) { MappingNormalization - Keys(Io, R); + Keys(Io, R); Io.mapRequired("FilePath", Keys->FilePath); Io.mapRequired("Offset", Keys->Offset); Io.mapRequired("Length", Keys->Length); diff --git a/clang/lib/DPCT/FileGenerator/GenFiles.cpp b/clang/lib/DPCT/FileGenerator/GenFiles.cpp index 69357d6632cc..654f7d0f202f 100644 --- a/clang/lib/DPCT/FileGenerator/GenFiles.cpp +++ b/clang/lib/DPCT/FileGenerator/GenFiles.cpp @@ -440,7 +440,6 @@ void processAllFiles(StringRef InRoot, StringRef OutRoot, } } - static void getMainSrcFilesRepls( std::vector &MainSrcFilesRepls) { auto &FileRelpsMap = DpctGlobalInfo::getFileRelpsMap(); diff --git a/clang/lib/DPCT/IncMigration/ExternalReplacement.cpp b/clang/lib/DPCT/IncMigration/ExternalReplacement.cpp index c2203dbef1ca..db9ededf6de6 100644 --- a/clang/lib/DPCT/IncMigration/ExternalReplacement.cpp +++ b/clang/lib/DPCT/IncMigration/ExternalReplacement.cpp @@ -159,7 +159,7 @@ int mergeExternalReps(clang::tooling::UnifiedPath InRootSrcFilePath, llvm::errs() << "Saved new version of " << YamlFile << " file\n"; std::vector Repls(Replaces.begin(), - Replaces.end()); + Replaces.end()); // For header file, its hash content digest and HasCUDASytax field is not // registed. diff --git a/clang/lib/DPCT/RulesLang/RulesLangNoneAPIAndType.cpp b/clang/lib/DPCT/RulesLang/RulesLangNoneAPIAndType.cpp index f2f56fe4907c..ac30a45b3d5e 100644 --- a/clang/lib/DPCT/RulesLang/RulesLangNoneAPIAndType.cpp +++ b/clang/lib/DPCT/RulesLang/RulesLangNoneAPIAndType.cpp @@ -318,8 +318,8 @@ void ConstantMemVarMigrationRule::runRule( } } -void ConstantMemVarMigrationRule::previousHCurrentD(const VarDecl *VD, - tooling::DpctReplacement &R) { +void ConstantMemVarMigrationRule::previousHCurrentD( + const VarDecl *VD, tooling::DpctReplacement &R) { // 1. emit DPCT1055 warning // 2. add a new variable for host // 3. insert dpct::constant_memory and add the info from that replacement @@ -371,8 +371,8 @@ void ConstantMemVarMigrationRule::previousHCurrentD(const VarDecl *VD, R = tooling::DpctReplacement(R.getFilePath(), 0, 0, ""); } -void ConstantMemVarMigrationRule::previousDCurrentH(const VarDecl *VD, - tooling::DpctReplacement &R) { +void ConstantMemVarMigrationRule::previousDCurrentH( + const VarDecl *VD, tooling::DpctReplacement &R) { // 1. change DeviceConstant to HostDeviceConstant // 2. emit DPCT1055 warning (warning info is from previous device case) // 3. add a new variable for host (decl info is from previous device case) @@ -401,7 +401,8 @@ void ConstantMemVarMigrationRule::previousDCurrentH(const VarDecl *VD, emplaceTransformation(new InsertText(SL, std::move(NewDecl))); } -void ConstantMemVarMigrationRule::removeHostConstantWarning(DpctReplacement &R) { +void ConstantMemVarMigrationRule::removeHostConstantWarning( + DpctReplacement &R) { std::string ReplStr = R.getReplacementText().str(); // warning text of Diagnostics::HOST_CONSTANT diff --git a/clang/lib/DPCT/TextModification.h b/clang/lib/DPCT/TextModification.h index 3192c9468908..eef0064c4bc7 100644 --- a/clang/lib/DPCT/TextModification.h +++ b/clang/lib/DPCT/TextModification.h @@ -41,7 +41,7 @@ enum ReplacementType { RT_ForSYCLMigration = 0, RT_CUDAWithCodePin }; class ExtReplacement : public tooling::DpctReplacement { public: /// Creates an invalid (not applicable) replacement. - ExtReplacement() : DpctReplacement(){}; + ExtReplacement() : DpctReplacement() {}; /// Creates a replacement of the range [Offset, Offset+Length) in /// FilePath with ReplacementText. @@ -49,9 +49,12 @@ class ExtReplacement : public tooling::DpctReplacement { /// \param FilePath A source file accessible via a SourceManager. /// \param Offset The byte offset of the start of the range in the file. /// \param Length The length of the range in bytes. - ExtReplacement(clang::tooling::UnifiedPath FilePath, unsigned Offset, unsigned Length, - StringRef ReplacementText, const TextModification *_TM) - : DpctReplacement(FilePath.getCanonicalPath(), Offset, Length, ReplacementText), TM(_TM) {} + ExtReplacement(clang::tooling::UnifiedPath FilePath, unsigned Offset, + unsigned Length, StringRef ReplacementText, + const TextModification *_TM) + : DpctReplacement(FilePath.getCanonicalPath(), Offset, Length, + ReplacementText), + TM(_TM) {} /// Creates a Replacement of the range [Start, Start+Length) with /// ReplacementText. From 2154ad539dcee21ce203126bd1cb12cae5515c6d Mon Sep 17 00:00:00 2001 From: "Jiang, Zhiwei" Date: Tue, 27 May 2025 11:07:09 +0800 Subject: [PATCH 05/53] Update Signed-off-by: Jiang, Zhiwei --- clang/lib/DPCT/CMakeLists.txt | 1 + clang/lib/DPCT/ReMigration/Hunk.cpp | 15 +++++++ clang/lib/DPCT/ReMigration/Hunk.h | 66 +++++++++++++++++++++++++++++ 3 files changed, 82 insertions(+) create mode 100644 clang/lib/DPCT/ReMigration/Hunk.cpp create mode 100644 clang/lib/DPCT/ReMigration/Hunk.h diff --git a/clang/lib/DPCT/CMakeLists.txt b/clang/lib/DPCT/CMakeLists.txt index 7576567119d2..8c699ff86271 100644 --- a/clang/lib/DPCT/CMakeLists.txt +++ b/clang/lib/DPCT/CMakeLists.txt @@ -271,6 +271,7 @@ add_clang_library(DPCT RulesMathLib/RandomAPIMigration.cpp RulesMathLib/SolverAPIMigration.cpp CodePin/GenCodePinHeader.cpp + ReMigration/Hunk.cpp DEPENDS ClangDriverOptions diff --git a/clang/lib/DPCT/ReMigration/Hunk.cpp b/clang/lib/DPCT/ReMigration/Hunk.cpp new file mode 100644 index 000000000000..d36167b771ef --- /dev/null +++ b/clang/lib/DPCT/ReMigration/Hunk.cpp @@ -0,0 +1,15 @@ +//===--------------------------- Hunk.cpp ---------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "Hunk.h" + +namespace clang { +namespace dpct { +// TODO +} // namespace dpct +} // namespace clang diff --git a/clang/lib/DPCT/ReMigration/Hunk.h b/clang/lib/DPCT/ReMigration/Hunk.h new file mode 100644 index 000000000000..ab8d0a651664 --- /dev/null +++ b/clang/lib/DPCT/ReMigration/Hunk.h @@ -0,0 +1,66 @@ +//===--------------------------- Hunk.h -----------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef CLANG_DPCT_HUNK_H +#define CLANG_DPCT_HUNK_H + +#include "clang/Tooling/Core/Replacement.h" + +namespace clang { +namespace dpct { +class Hunk { +public: + enum HunkType : unsigned { ModifyFile = 0, AddFile, DeleteFile, MoveFile }; + +private: + HunkType HT; + +public: + Hunk(HunkType HT) : HT(HT) {} + HunkType getHunkType() const { return HT; } + virtual ~Hunk() = default; +}; + +class ModifyFileHunk : public Hunk, public clang::tooling::Replacement { +public: + ModifyFileHunk(const std::string &FilePath, unsigned Offset, unsigned Length, + const std::string &ReplacementText) + : Hunk(ModifyFile), + Replacement(FilePath, Offset, Length, ReplacementText) {} +}; + +class AddFileHunk : public Hunk { + std::string NewFilePath; + +public: + AddFileHunk(std::string NewFilePath) + : Hunk(AddFile), NewFilePath(std::move(NewFilePath)) {} +}; + +class DeleteFileHunk : public Hunk { + std::string OldFilePath; + +public: + DeleteFileHunk(std::string OldFilePath) + : Hunk(DeleteFile), OldFilePath(std::move(OldFilePath)) {} +}; + +class MoveFileHunk : public Hunk, public clang::tooling::Replacement { + std::string NewFilePath; + +public: + MoveFileHunk(const std::string &FilePath, unsigned Offset, unsigned Length, + const std::string &ReplacementText, + const std::string &NewFilePath) + : Hunk(MoveFile), Replacement(FilePath, Offset, Length, ReplacementText), + NewFilePath(std::move(NewFilePath)) {} +}; +} // namespace dpct +} // namespace clang + +#endif // CLANG_DPCT_HUNK_H From 6601a024b5a90d26e40ee9eb94f9331281baf0c7 Mon Sep 17 00:00:00 2001 From: "Jiang, Zhiwei" Date: Tue, 27 May 2025 13:50:17 +0800 Subject: [PATCH 06/53] Fix Signed-off-by: Jiang, Zhiwei --- clang/include/clang/Tooling/Core/Replacement.h | 5 ++--- clang/lib/Tooling/Core/Replacement.cpp | 6 ++++++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/clang/include/clang/Tooling/Core/Replacement.h b/clang/include/clang/Tooling/Core/Replacement.h index 41c2baa5903d..ed45c4cce4e7 100644 --- a/clang/include/clang/Tooling/Core/Replacement.h +++ b/clang/include/clang/Tooling/Core/Replacement.h @@ -278,10 +278,9 @@ inline bool operator!=(const Replacement &LHS, const Replacement &RHS) { class Replacements { private: #ifdef SYCLomatic_CUSTOMIZATION - using ReplacementsImpl = std::set; -#else - using ReplacementsImpl = std::set; + using Replacement = DpctReplacement; #endif // SYCLomatic_CUSTOMIZATION + using ReplacementsImpl = std::set; public: using const_iterator = ReplacementsImpl::const_iterator; diff --git a/clang/lib/Tooling/Core/Replacement.cpp b/clang/lib/Tooling/Core/Replacement.cpp index 76450d6c25db..19e96a2905b7 100644 --- a/clang/lib/Tooling/Core/Replacement.cpp +++ b/clang/lib/Tooling/Core/Replacement.cpp @@ -181,6 +181,9 @@ void Replacement::setFromSourceRange(const SourceManager &Sources, ReplacementText); } +#ifdef SYCLomatic_CUSTOMIZATION +#define Replacement DpctReplacement +#endif // SYCLomatic_CUSTOMIZATION Replacement Replacements::getReplacementInChangedCode(const Replacement &R) const { unsigned NewStart = getShiftedCodePosition(R.getOffset()); @@ -723,6 +726,9 @@ std::map groupReplacementsByFile( } return Result; } +#ifdef SYCLomatic_CUSTOMIZATION +#undef Replacement +#endif // SYCLomatic_CUSTOMIZATION } // namespace tooling } // namespace clang From 9bbe00ab438b3291970b8138dc2a60ed9b46ce62 Mon Sep 17 00:00:00 2001 From: "Jiang, Zhiwei" Date: Wed, 28 May 2025 08:54:44 +0800 Subject: [PATCH 07/53] Fix build Signed-off-by: Jiang, Zhiwei --- .../include/clang/Tooling/Core/Replacement.h | 70 +++++++++- .../include/clang/Tooling/ReplacementsYaml.h | 127 +++++++++++++++++- .../DPCT/IncMigration/ExternalReplacement.cpp | 12 +- .../DPCT/IncMigration/ExternalReplacement.h | 2 + clang/lib/DPCT/ReMigration/Hunk.h | 54 -------- 5 files changed, 201 insertions(+), 64 deletions(-) diff --git a/clang/include/clang/Tooling/Core/Replacement.h b/clang/include/clang/Tooling/Core/Replacement.h index ed45c4cce4e7..5da84f94eaa1 100644 --- a/clang/include/clang/Tooling/Core/Replacement.h +++ b/clang/include/clang/Tooling/Core/Replacement.h @@ -54,7 +54,7 @@ enum class ConstantFlagType : int { HostDeviceInOnePass = 4 }; enum class HelperFileEnum : unsigned int; -} +} // namespace dpct #endif // SYCLomatic_CUSTOMIZATION namespace tooling { @@ -443,6 +443,70 @@ struct OptionInfo { std::vector ValueVec; bool Specified = true; }; + +class Hunk { +public: + enum HunkType : unsigned { + ModifyFile = 0, + AddFile, + DeleteFile, + MoveFile, + Unspecified + }; + +private: + HunkType HT = Unspecified; + +public: + Hunk(HunkType HT) : HT(HT) {} + HunkType getHunkType() const { return HT; } + virtual ~Hunk() = default; +}; + +class ModifyFileHunk : public Hunk, public Replacement { +public: + ModifyFileHunk() : Hunk(ModifyFile), Replacement() {} + ModifyFileHunk(const std::string &FilePath, unsigned Offset, unsigned Length, + const std::string &ReplacementText) + : Hunk(ModifyFile), + Replacement(FilePath, Offset, Length, ReplacementText) {} + ModifyFileHunk(const Replacement &R) : Hunk(ModifyFile), Replacement(R) {} +}; + +class AddFileHunk : public Hunk { + std::string NewFilePath; + +public: + AddFileHunk() : Hunk(AddFile) {} + AddFileHunk(std::string NewFilePath) + : Hunk(AddFile), NewFilePath(std::move(NewFilePath)) {} + const std::string &getNewFilePath() const { return NewFilePath; } +}; + +class DeleteFileHunk : public Hunk { + std::string OldFilePath; + +public: + DeleteFileHunk() : Hunk(DeleteFile) {} + DeleteFileHunk(std::string OldFilePath) + : Hunk(DeleteFile), OldFilePath(std::move(OldFilePath)) {} + const std::string &getOldFilePath() const { return OldFilePath; } +}; + +class MoveFileHunk : public Hunk, public Replacement { + std::string NewFilePath; + +public: + MoveFileHunk() : Hunk(MoveFile), Replacement() {} + MoveFileHunk(const std::string &FilePath, unsigned Offset, unsigned Length, + const std::string &ReplacementText, + const std::string &NewFilePath) + : Hunk(MoveFile), Replacement(FilePath, Offset, Length, ReplacementText), + NewFilePath(std::move(NewFilePath)) {} + MoveFileHunk(const Replacement &R, const std::string &NewFilePath) + : Hunk(MoveFile), Replacement(R), NewFilePath(NewFilePath) {} + std::string getNewFilePath() const { return NewFilePath; } +}; #endif // SYCLomatic_CUSTOMIZATION /// Collection of Replacements generated from a single translation unit. struct TranslationUnitReplacements { @@ -462,6 +526,10 @@ struct TranslationUnitReplacements { std::map> CompileTargets; std::map OptionMap; std::vector Replacements; + std::vector ModifyFileHunks; + std::vector AddFileHunks; + std::vector DeleteFileHunks; + std::vector MoveFileHunks; #else std::vector Replacements; #endif // SYCLomatic_CUSTOMIZATION diff --git a/clang/include/clang/Tooling/ReplacementsYaml.h b/clang/include/clang/Tooling/ReplacementsYaml.h index ab2add9f6c42..fafdeec9026f 100644 --- a/clang/include/clang/Tooling/ReplacementsYaml.h +++ b/clang/include/clang/Tooling/ReplacementsYaml.h @@ -32,6 +32,25 @@ LLVM_YAML_IS_SEQUENCE_VECTOR(clang::tooling::MainSourceFileInfo) LLVM_YAML_IS_STRING_MAP(std::vector) LLVM_YAML_IS_STRING_MAP(clang::tooling::OptionInfo) LLVM_YAML_IS_SEQUENCE_VECTOR(clang::tooling::DpctReplacement) +LLVM_YAML_IS_SEQUENCE_VECTOR(clang::tooling::ModifyFileHunk) +LLVM_YAML_IS_SEQUENCE_VECTOR(clang::tooling::AddFileHunk) +LLVM_YAML_IS_SEQUENCE_VECTOR(clang::tooling::DeleteFileHunk) +LLVM_YAML_IS_SEQUENCE_VECTOR(clang::tooling::MoveFileHunk) +LLVM_YAML_DECLARE_ENUM_TRAITS(clang::tooling::Hunk::HunkType) +namespace llvm { +namespace yaml { +inline void +ScalarEnumerationTraits::enumeration( + IO &io, clang::tooling::Hunk::HunkType &value) { + io.enumCase(value, "ModifyFile", clang::tooling::Hunk::HunkType::ModifyFile); + io.enumCase(value, "AddFile", clang::tooling::Hunk::HunkType::AddFile); + io.enumCase(value, "DeleteFile", clang::tooling::Hunk::HunkType::DeleteFile); + io.enumCase(value, "MoveFile", clang::tooling::Hunk::HunkType::MoveFile); + io.enumCase(value, "Unspecified", + clang::tooling::Hunk::HunkType::Unspecified); +} +} // namespace yaml +} // namespace llvm #endif // SYCLomatic_CUSTOMIZATION namespace llvm { @@ -96,7 +115,7 @@ template <> struct MappingTraits { } clang::tooling::DpctReplacement denormalize(const IO &io) { - clang::tooling::DpctReplacement R = Base.denormalize(io); + clang::tooling::DpctReplacement R(Base.denormalize(io)); if (ConstantFlag == "HostDeviceConstant") { R.setConstantFlag(clang::dpct::ConstantFlagType::HostDevice); } else if (ConstantFlag == "DeviceConstant") { @@ -133,9 +152,7 @@ template <> struct MappingTraits { Io.mapOptional("BlockLevelFormatFlag", Keys->BlockLevelFormatFlag); } }; -#endif // SYCLomatic_CUSTOMIZATION -#ifdef SYCLomatic_CUSTOMIZATION template <> struct MappingTraits { struct NormalizedMainSourceFilesDigest { @@ -229,6 +246,106 @@ template <> struct MappingTraits { } }; +template <> struct MappingTraits { + struct NormalizedModifyFileHunk { + NormalizedModifyFileHunk(const IO &io) + : HT(clang::tooling::Hunk::HunkType::Unspecified), Base(io) {} + NormalizedModifyFileHunk(const IO &io, clang::tooling::ModifyFileHunk &H) + : HT(H.getHunkType()), + Base(io, static_cast(H)) {} + + clang::tooling::ModifyFileHunk denormalize(const IO &io) { + clang::tooling::ModifyFileHunk H(Base.denormalize(io)); + return H; + } + + clang::tooling::Hunk::HunkType HT; + MappingTraits::NormalizedReplacement Base; + }; + + static void mapping(IO &Io, clang::tooling::ModifyFileHunk &H) { + MappingNormalization + Keys(Io, H); + MappingTraits::mapping(Io, H); + Io.mapOptional("HunkType", Keys->HT); + } +}; + +template <> struct MappingTraits { + struct NormalizedAddFileHunk { + NormalizedAddFileHunk(const IO &io) + : HT(clang::tooling::Hunk::HunkType::Unspecified), NewFilePath("") {} + NormalizedAddFileHunk(const IO &io, clang::tooling::AddFileHunk &H) + : HT(H.getHunkType()), NewFilePath(H.getNewFilePath()) {} + + clang::tooling::AddFileHunk denormalize(const IO &io) { + clang::tooling::AddFileHunk H(NewFilePath); + return H; + } + + clang::tooling::Hunk::HunkType HT; + std::string NewFilePath; + }; + static void mapping(IO &Io, clang::tooling::AddFileHunk &H) { + MappingNormalization Keys( + Io, H); + Io.mapOptional("HunkType", Keys->HT); + Io.mapOptional("NewFilePath", Keys->NewFilePath); + } +}; + +template <> struct MappingTraits { + struct NormalizedDeleteFileHunk { + NormalizedDeleteFileHunk(const IO &io) + : HT(clang::tooling::Hunk::HunkType::Unspecified), OldFilePath("") {} + NormalizedDeleteFileHunk(const IO &io, clang::tooling::DeleteFileHunk &H) + : HT(H.getHunkType()), OldFilePath(H.getOldFilePath()) {} + + clang::tooling::DeleteFileHunk denormalize(const IO &io) { + clang::tooling::DeleteFileHunk H(OldFilePath); + return H; + } + + clang::tooling::Hunk::HunkType HT; + std::string OldFilePath; + }; + static void mapping(IO &Io, clang::tooling::DeleteFileHunk &H) { + MappingNormalization + Keys(Io, H); + Io.mapOptional("HunkType", Keys->HT); + Io.mapOptional("OldFilePath", Keys->OldFilePath); + } +}; + +template <> struct MappingTraits { + struct NormalizedMoveFileHunk { + NormalizedMoveFileHunk(const IO &io) + : HT(clang::tooling::Hunk::HunkType::Unspecified), Base(io), + NewFilePath("") {} + NormalizedMoveFileHunk(const IO &io, clang::tooling::MoveFileHunk &H) + : HT(H.getHunkType()), + Base(io, static_cast(H)), + NewFilePath(H.getNewFilePath()) {} + + clang::tooling::MoveFileHunk denormalize(const IO &io) { + clang::tooling::MoveFileHunk H(Base.denormalize(io), NewFilePath); + return H; + } + + clang::tooling::Hunk::HunkType HT; + MappingTraits::NormalizedReplacement Base; + std::string NewFilePath; + }; + + static void mapping(IO &Io, clang::tooling::MoveFileHunk &H) { + MappingNormalization + Keys(Io, H); + MappingTraits::mapping(Io, H); + Io.mapOptional("HunkType", Keys->HT); + Io.mapOptional("NewFilePath", Keys->NewFilePath); + } +}; + // Keep here only for backward compatibility - begin template <> struct MappingTraits { struct NormalizedHelperFuncForYaml { @@ -285,6 +402,10 @@ template <> struct MappingTraits { // Keep here only for backward compatibility - end Io.mapOptional("CompileTargets", Doc.CompileTargets); Io.mapOptional("OptionMap", Doc.OptionMap); + Io.mapOptional("ModifyFileHunks", Doc.ModifyFileHunks); + Io.mapOptional("AddFileHunks", Doc.AddFileHunks); + Io.mapOptional("DeleteFileHunks", Doc.DeleteFileHunks); + Io.mapOptional("MoveFileHunks", Doc.MoveFileHunks); #endif } }; diff --git a/clang/lib/DPCT/IncMigration/ExternalReplacement.cpp b/clang/lib/DPCT/IncMigration/ExternalReplacement.cpp index db9ededf6de6..ab365aa7e6ca 100644 --- a/clang/lib/DPCT/IncMigration/ExternalReplacement.cpp +++ b/clang/lib/DPCT/IncMigration/ExternalReplacement.cpp @@ -10,19 +10,19 @@ // -Load replacement from external (disk file) // -Merge replacement in current migration with previous migration. +#include "ExternalReplacement.h" #include "AnalysisInfo.h" +#include "IncMigration/IncrementalMigrationUtility.h" #include "Utility.h" + +#include "clang/Tooling/Core/Diagnostic.h" #include "clang/Tooling/Core/Replacement.h" +#include "clang/Tooling/Refactoring.h" +#include "clang/Tooling/ReplacementsYaml.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Path.h" - -#include "ExternalReplacement.h" -#include "IncMigration/IncrementalMigrationUtility.h" -#include "clang/Tooling/Core/Diagnostic.h" -#include "clang/Tooling/Refactoring.h" -#include "clang/Tooling/ReplacementsYaml.h" #include "llvm/Support/YAMLTraits.h" #include "llvm/Support/raw_os_ostream.h" diff --git a/clang/lib/DPCT/IncMigration/ExternalReplacement.h b/clang/lib/DPCT/IncMigration/ExternalReplacement.h index 3a7795c60ab1..39c27a378fdb 100644 --- a/clang/lib/DPCT/IncMigration/ExternalReplacement.h +++ b/clang/lib/DPCT/IncMigration/ExternalReplacement.h @@ -10,7 +10,9 @@ #define __EXTERNAL_REPLACEMENT_H__ #include "clang/Tooling/Core/Replacement.h" +#include "clang/Tooling/Core/UnifiedPath.h" #include "llvm/ADT/StringRef.h" + #include #include diff --git a/clang/lib/DPCT/ReMigration/Hunk.h b/clang/lib/DPCT/ReMigration/Hunk.h index ab8d0a651664..bd62c6cf7e0c 100644 --- a/clang/lib/DPCT/ReMigration/Hunk.h +++ b/clang/lib/DPCT/ReMigration/Hunk.h @@ -9,58 +9,4 @@ #ifndef CLANG_DPCT_HUNK_H #define CLANG_DPCT_HUNK_H -#include "clang/Tooling/Core/Replacement.h" - -namespace clang { -namespace dpct { -class Hunk { -public: - enum HunkType : unsigned { ModifyFile = 0, AddFile, DeleteFile, MoveFile }; - -private: - HunkType HT; - -public: - Hunk(HunkType HT) : HT(HT) {} - HunkType getHunkType() const { return HT; } - virtual ~Hunk() = default; -}; - -class ModifyFileHunk : public Hunk, public clang::tooling::Replacement { -public: - ModifyFileHunk(const std::string &FilePath, unsigned Offset, unsigned Length, - const std::string &ReplacementText) - : Hunk(ModifyFile), - Replacement(FilePath, Offset, Length, ReplacementText) {} -}; - -class AddFileHunk : public Hunk { - std::string NewFilePath; - -public: - AddFileHunk(std::string NewFilePath) - : Hunk(AddFile), NewFilePath(std::move(NewFilePath)) {} -}; - -class DeleteFileHunk : public Hunk { - std::string OldFilePath; - -public: - DeleteFileHunk(std::string OldFilePath) - : Hunk(DeleteFile), OldFilePath(std::move(OldFilePath)) {} -}; - -class MoveFileHunk : public Hunk, public clang::tooling::Replacement { - std::string NewFilePath; - -public: - MoveFileHunk(const std::string &FilePath, unsigned Offset, unsigned Length, - const std::string &ReplacementText, - const std::string &NewFilePath) - : Hunk(MoveFile), Replacement(FilePath, Offset, Length, ReplacementText), - NewFilePath(std::move(NewFilePath)) {} -}; -} // namespace dpct -} // namespace clang - #endif // CLANG_DPCT_HUNK_H From 5a6147dce5b605696abf428e8c3b4fb023b67328 Mon Sep 17 00:00:00 2001 From: "Jiang, Zhiwei" Date: Wed, 28 May 2025 10:21:00 +0800 Subject: [PATCH 08/53] Fix load Signed-off-by: Jiang, Zhiwei --- .../include/clang/Tooling/ReplacementsYaml.h | 63 +++++++++++++------ 1 file changed, 43 insertions(+), 20 deletions(-) diff --git a/clang/include/clang/Tooling/ReplacementsYaml.h b/clang/include/clang/Tooling/ReplacementsYaml.h index fafdeec9026f..57b65edcfffd 100644 --- a/clang/include/clang/Tooling/ReplacementsYaml.h +++ b/clang/include/clang/Tooling/ReplacementsYaml.h @@ -93,12 +93,13 @@ template <> struct MappingTraits { template <> struct MappingTraits { struct NormalizedDpctReplacement { NormalizedDpctReplacement(const IO &io) - : Base(io), ConstantFlag(""), ConstantOffset(0), InitStr(""), - NewHostVarName(""), BlockLevelFormatFlag(false) {} + : Offset(0), Length(0), ConstantFlag(""), ConstantOffset(0), + InitStr(""), NewHostVarName(""), BlockLevelFormatFlag(false) {} NormalizedDpctReplacement(const IO &io, const clang::tooling::DpctReplacement &R) - : Base(io, static_cast(R)), + : FilePath(R.getFilePath()), Offset(R.getOffset()), + Length(R.getLength()), ReplacementText(R.getReplacementText()), ConstantOffset(R.getConstantOffset()), InitStr(R.getInitStr()), NewHostVarName(R.getNewHostVarName()), BlockLevelFormatFlag(R.getBlockLevelFormatFlag()) { @@ -114,8 +115,9 @@ template <> struct MappingTraits { } } - clang::tooling::DpctReplacement denormalize(const IO &io) { - clang::tooling::DpctReplacement R(Base.denormalize(io)); + clang::tooling::DpctReplacement denormalize(const IO &) { + clang::tooling::DpctReplacement R(FilePath, Offset, Length, + ReplacementText); if (ConstantFlag == "HostDeviceConstant") { R.setConstantFlag(clang::dpct::ConstantFlagType::HostDevice); } else if (ConstantFlag == "DeviceConstant") { @@ -132,7 +134,10 @@ template <> struct MappingTraits { return R; } - MappingTraits::NormalizedReplacement Base; + std::string FilePath; + unsigned int Offset; + unsigned int Length; + std::string ReplacementText; std::string ConstantFlag = ""; unsigned int ConstantOffset = 0; std::string InitStr = ""; @@ -144,7 +149,10 @@ template <> struct MappingTraits { MappingNormalization Keys(Io, R); - MappingTraits::mapping(Io, R); + Io.mapRequired("FilePath", Keys->FilePath); + Io.mapRequired("Offset", Keys->Offset); + Io.mapRequired("Length", Keys->Length); + Io.mapRequired("ReplacementText", Keys->ReplacementText); Io.mapOptional("ConstantFlag", Keys->ConstantFlag); Io.mapOptional("ConstantOffset", Keys->ConstantOffset); Io.mapOptional("InitStr", Keys->InitStr); @@ -249,25 +257,33 @@ template <> struct MappingTraits { template <> struct MappingTraits { struct NormalizedModifyFileHunk { NormalizedModifyFileHunk(const IO &io) - : HT(clang::tooling::Hunk::HunkType::Unspecified), Base(io) {} + : HT(clang::tooling::Hunk::HunkType::Unspecified), Offset(0), + Length(0) {} NormalizedModifyFileHunk(const IO &io, clang::tooling::ModifyFileHunk &H) - : HT(H.getHunkType()), - Base(io, static_cast(H)) {} + : HT(H.getHunkType()), FilePath(H.getFilePath()), Offset(H.getOffset()), + Length(H.getLength()), ReplacementText(H.getReplacementText()) {} - clang::tooling::ModifyFileHunk denormalize(const IO &io) { - clang::tooling::ModifyFileHunk H(Base.denormalize(io)); + clang::tooling::ModifyFileHunk denormalize(const IO &) { + clang::tooling::ModifyFileHunk H(FilePath, Offset, Length, + ReplacementText); return H; } clang::tooling::Hunk::HunkType HT; - MappingTraits::NormalizedReplacement Base; + std::string FilePath; + unsigned int Offset; + unsigned int Length; + std::string ReplacementText; }; static void mapping(IO &Io, clang::tooling::ModifyFileHunk &H) { MappingNormalization Keys(Io, H); - MappingTraits::mapping(Io, H); Io.mapOptional("HunkType", Keys->HT); + Io.mapRequired("FilePath", Keys->FilePath); + Io.mapRequired("Offset", Keys->Offset); + Io.mapRequired("Length", Keys->Length); + Io.mapRequired("ReplacementText", Keys->ReplacementText); } }; @@ -320,28 +336,35 @@ template <> struct MappingTraits { template <> struct MappingTraits { struct NormalizedMoveFileHunk { NormalizedMoveFileHunk(const IO &io) - : HT(clang::tooling::Hunk::HunkType::Unspecified), Base(io), + : HT(clang::tooling::Hunk::HunkType::Unspecified), Offset(0), Length(0), NewFilePath("") {} NormalizedMoveFileHunk(const IO &io, clang::tooling::MoveFileHunk &H) - : HT(H.getHunkType()), - Base(io, static_cast(H)), + : HT(H.getHunkType()), FilePath(H.getFilePath()), Offset(H.getOffset()), + Length(H.getLength()), ReplacementText(H.getReplacementText()), NewFilePath(H.getNewFilePath()) {} clang::tooling::MoveFileHunk denormalize(const IO &io) { - clang::tooling::MoveFileHunk H(Base.denormalize(io), NewFilePath); + clang::tooling::MoveFileHunk H(FilePath, Offset, Length, ReplacementText, + NewFilePath); return H; } clang::tooling::Hunk::HunkType HT; - MappingTraits::NormalizedReplacement Base; + std::string FilePath; + unsigned int Offset; + unsigned int Length; + std::string ReplacementText; std::string NewFilePath; }; static void mapping(IO &Io, clang::tooling::MoveFileHunk &H) { MappingNormalization Keys(Io, H); - MappingTraits::mapping(Io, H); Io.mapOptional("HunkType", Keys->HT); + Io.mapRequired("FilePath", Keys->FilePath); + Io.mapRequired("Offset", Keys->Offset); + Io.mapRequired("Length", Keys->Length); + Io.mapRequired("ReplacementText", Keys->ReplacementText); Io.mapOptional("NewFilePath", Keys->NewFilePath); } }; From 92b13992bf5e0aeb4e00f80d50becc5ad429c4dd Mon Sep 17 00:00:00 2001 From: "Jiang, Zhiwei" Date: Wed, 28 May 2025 13:07:26 +0800 Subject: [PATCH 09/53] Update Signed-off-by: Jiang, Zhiwei --- .../include/clang/Tooling/ReplacementsYaml.h | 46 +++++++------------ 1 file changed, 17 insertions(+), 29 deletions(-) diff --git a/clang/include/clang/Tooling/ReplacementsYaml.h b/clang/include/clang/Tooling/ReplacementsYaml.h index 57b65edcfffd..eb85b408c58b 100644 --- a/clang/include/clang/Tooling/ReplacementsYaml.h +++ b/clang/include/clang/Tooling/ReplacementsYaml.h @@ -256,11 +256,9 @@ template <> struct MappingTraits { template <> struct MappingTraits { struct NormalizedModifyFileHunk { - NormalizedModifyFileHunk(const IO &io) - : HT(clang::tooling::Hunk::HunkType::Unspecified), Offset(0), - Length(0) {} + NormalizedModifyFileHunk(const IO &io) : Offset(0), Length(0) {} NormalizedModifyFileHunk(const IO &io, clang::tooling::ModifyFileHunk &H) - : HT(H.getHunkType()), FilePath(H.getFilePath()), Offset(H.getOffset()), + : FilePath(H.getFilePath()), Offset(H.getOffset()), Length(H.getLength()), ReplacementText(H.getReplacementText()) {} clang::tooling::ModifyFileHunk denormalize(const IO &) { @@ -269,7 +267,6 @@ template <> struct MappingTraits { return H; } - clang::tooling::Hunk::HunkType HT; std::string FilePath; unsigned int Offset; unsigned int Length; @@ -277,9 +274,9 @@ template <> struct MappingTraits { }; static void mapping(IO &Io, clang::tooling::ModifyFileHunk &H) { - MappingNormalization + MappingNormalization Keys(Io, H); - Io.mapOptional("HunkType", Keys->HT); Io.mapRequired("FilePath", Keys->FilePath); Io.mapRequired("Offset", Keys->Offset); Io.mapRequired("Length", Keys->Length); @@ -289,57 +286,50 @@ template <> struct MappingTraits { template <> struct MappingTraits { struct NormalizedAddFileHunk { - NormalizedAddFileHunk(const IO &io) - : HT(clang::tooling::Hunk::HunkType::Unspecified), NewFilePath("") {} + NormalizedAddFileHunk(const IO &io) {} NormalizedAddFileHunk(const IO &io, clang::tooling::AddFileHunk &H) - : HT(H.getHunkType()), NewFilePath(H.getNewFilePath()) {} + : NewFilePath(H.getNewFilePath()) {} clang::tooling::AddFileHunk denormalize(const IO &io) { clang::tooling::AddFileHunk H(NewFilePath); return H; } - clang::tooling::Hunk::HunkType HT; std::string NewFilePath; }; static void mapping(IO &Io, clang::tooling::AddFileHunk &H) { - MappingNormalization Keys( - Io, H); - Io.mapOptional("HunkType", Keys->HT); - Io.mapOptional("NewFilePath", Keys->NewFilePath); + MappingNormalization + Keys(Io, H); + Io.mapRequired("NewFilePath", Keys->NewFilePath); } }; template <> struct MappingTraits { struct NormalizedDeleteFileHunk { - NormalizedDeleteFileHunk(const IO &io) - : HT(clang::tooling::Hunk::HunkType::Unspecified), OldFilePath("") {} + NormalizedDeleteFileHunk(const IO &io) {} NormalizedDeleteFileHunk(const IO &io, clang::tooling::DeleteFileHunk &H) - : HT(H.getHunkType()), OldFilePath(H.getOldFilePath()) {} + : OldFilePath(H.getOldFilePath()) {} clang::tooling::DeleteFileHunk denormalize(const IO &io) { clang::tooling::DeleteFileHunk H(OldFilePath); return H; } - clang::tooling::Hunk::HunkType HT; std::string OldFilePath; }; static void mapping(IO &Io, clang::tooling::DeleteFileHunk &H) { - MappingNormalization + MappingNormalization Keys(Io, H); - Io.mapOptional("HunkType", Keys->HT); - Io.mapOptional("OldFilePath", Keys->OldFilePath); + Io.mapRequired("OldFilePath", Keys->OldFilePath); } }; template <> struct MappingTraits { struct NormalizedMoveFileHunk { - NormalizedMoveFileHunk(const IO &io) - : HT(clang::tooling::Hunk::HunkType::Unspecified), Offset(0), Length(0), - NewFilePath("") {} + NormalizedMoveFileHunk(const IO &io) : Offset(0), Length(0) {} NormalizedMoveFileHunk(const IO &io, clang::tooling::MoveFileHunk &H) - : HT(H.getHunkType()), FilePath(H.getFilePath()), Offset(H.getOffset()), + : FilePath(H.getFilePath()), Offset(H.getOffset()), Length(H.getLength()), ReplacementText(H.getReplacementText()), NewFilePath(H.getNewFilePath()) {} @@ -349,7 +339,6 @@ template <> struct MappingTraits { return H; } - clang::tooling::Hunk::HunkType HT; std::string FilePath; unsigned int Offset; unsigned int Length; @@ -360,12 +349,11 @@ template <> struct MappingTraits { static void mapping(IO &Io, clang::tooling::MoveFileHunk &H) { MappingNormalization Keys(Io, H); - Io.mapOptional("HunkType", Keys->HT); Io.mapRequired("FilePath", Keys->FilePath); Io.mapRequired("Offset", Keys->Offset); Io.mapRequired("Length", Keys->Length); Io.mapRequired("ReplacementText", Keys->ReplacementText); - Io.mapOptional("NewFilePath", Keys->NewFilePath); + Io.mapRequired("NewFilePath", Keys->NewFilePath); } }; From 8d4a7836a86cc28f28513d1d0746cc6cc666d278 Mon Sep 17 00:00:00 2001 From: "Jiang, Zhiwei" Date: Wed, 28 May 2025 14:15:24 +0800 Subject: [PATCH 10/53] Update Signed-off-by: Jiang, Zhiwei --- .../include/clang/Tooling/Core/Replacement.h | 11 +++++--- .../include/clang/Tooling/ReplacementsYaml.h | 13 ++++++--- clang/lib/DPCT/CMakeLists.txt | 2 +- clang/lib/DPCT/DPCT.cpp | 4 ++- clang/lib/DPCT/FileGenerator/GenFiles.cpp | 2 +- .../DPCT/IncMigration/ExternalReplacement.cpp | 26 +++++++++++++---- .../DPCT/IncMigration/ExternalReplacement.h | 7 +++-- .../IncrementalMigrationUtility.cpp | 2 +- clang/lib/DPCT/IncMigration/ReMigratiom.cpp | 28 +++++++++++++++++++ clang/lib/DPCT/ReMigration/Hunk.cpp | 15 ---------- clang/lib/DPCT/ReMigration/Hunk.h | 12 -------- 11 files changed, 76 insertions(+), 46 deletions(-) create mode 100644 clang/lib/DPCT/IncMigration/ReMigratiom.cpp delete mode 100644 clang/lib/DPCT/ReMigration/Hunk.cpp delete mode 100644 clang/lib/DPCT/ReMigration/Hunk.h diff --git a/clang/include/clang/Tooling/Core/Replacement.h b/clang/include/clang/Tooling/Core/Replacement.h index 5da84f94eaa1..37e1d8934725 100644 --- a/clang/include/clang/Tooling/Core/Replacement.h +++ b/clang/include/clang/Tooling/Core/Replacement.h @@ -507,6 +507,13 @@ class MoveFileHunk : public Hunk, public Replacement { : Hunk(MoveFile), Replacement(R), NewFilePath(NewFilePath) {} std::string getNewFilePath() const { return NewFilePath; } }; + +struct GitDiffChanges { + std::vector ModifyFileHunks; + std::vector AddFileHunks; + std::vector DeleteFileHunks; + std::vector MoveFileHunks; +}; #endif // SYCLomatic_CUSTOMIZATION /// Collection of Replacements generated from a single translation unit. struct TranslationUnitReplacements { @@ -526,10 +533,6 @@ struct TranslationUnitReplacements { std::map> CompileTargets; std::map OptionMap; std::vector Replacements; - std::vector ModifyFileHunks; - std::vector AddFileHunks; - std::vector DeleteFileHunks; - std::vector MoveFileHunks; #else std::vector Replacements; #endif // SYCLomatic_CUSTOMIZATION diff --git a/clang/include/clang/Tooling/ReplacementsYaml.h b/clang/include/clang/Tooling/ReplacementsYaml.h index eb85b408c58b..da7e2e45db4c 100644 --- a/clang/include/clang/Tooling/ReplacementsYaml.h +++ b/clang/include/clang/Tooling/ReplacementsYaml.h @@ -357,6 +357,15 @@ template <> struct MappingTraits { } }; +template <> struct MappingTraits { + static void mapping(IO &Io, clang::tooling::GitDiffChanges &GDC) { + Io.mapOptional("ModifyFileHunks", GDC.ModifyFileHunks); + Io.mapOptional("AddFileHunks", GDC.AddFileHunks); + Io.mapOptional("DeleteFileHunks", GDC.DeleteFileHunks); + Io.mapOptional("MoveFileHunks", GDC.MoveFileHunks); + } +}; + // Keep here only for backward compatibility - begin template <> struct MappingTraits { struct NormalizedHelperFuncForYaml { @@ -413,10 +422,6 @@ template <> struct MappingTraits { // Keep here only for backward compatibility - end Io.mapOptional("CompileTargets", Doc.CompileTargets); Io.mapOptional("OptionMap", Doc.OptionMap); - Io.mapOptional("ModifyFileHunks", Doc.ModifyFileHunks); - Io.mapOptional("AddFileHunks", Doc.AddFileHunks); - Io.mapOptional("DeleteFileHunks", Doc.DeleteFileHunks); - Io.mapOptional("MoveFileHunks", Doc.MoveFileHunks); #endif } }; diff --git a/clang/lib/DPCT/CMakeLists.txt b/clang/lib/DPCT/CMakeLists.txt index 8c699ff86271..b84814b2ebf3 100644 --- a/clang/lib/DPCT/CMakeLists.txt +++ b/clang/lib/DPCT/CMakeLists.txt @@ -233,6 +233,7 @@ add_clang_library(DPCT MigrateScript/GenMakefile.cpp RulesInclude/InclusionHeaders.cpp IncMigration/IncrementalMigrationUtility.cpp + IncMigration/ReMigration.cpp UserDefinedRules/UserDefinedRules.cpp UserDefinedRules/PatternRewriter.cpp MigrateScript/MigrateBuildScript.cpp @@ -271,7 +272,6 @@ add_clang_library(DPCT RulesMathLib/RandomAPIMigration.cpp RulesMathLib/SolverAPIMigration.cpp CodePin/GenCodePinHeader.cpp - ReMigration/Hunk.cpp DEPENDS ClangDriverOptions diff --git a/clang/lib/DPCT/DPCT.cpp b/clang/lib/DPCT/DPCT.cpp index 85ce5abc7f91..41cb6abac9bb 100644 --- a/clang/lib/DPCT/DPCT.cpp +++ b/clang/lib/DPCT/DPCT.cpp @@ -521,7 +521,7 @@ static void loadMainSrcFileInfo(clang::tooling::UnifiedPath OutRoot) { DpctGlobalInfo::getYamlFileName()); auto PreTU = std::make_shared(); if (llvm::sys::fs::exists(YamlFilePath)) { - if (loadFromYaml(YamlFilePath, *PreTU) != 0) { + if (loadTUFromYaml(YamlFilePath, *PreTU) != 0) { llvm::errs() << getLoadYamlFailWarning(YamlFilePath); } @@ -1400,6 +1400,8 @@ int runDPCT(int argc, const char **argv) { dpctExit(MigrationSucceeded, false); } + tryLoadingUpstreamChangesAndUserChanges(); + ReplTy ReplCUDA, ReplSYCL; volatile int RunCount = 0; do { diff --git a/clang/lib/DPCT/FileGenerator/GenFiles.cpp b/clang/lib/DPCT/FileGenerator/GenFiles.cpp index 654f7d0f202f..21eb962f15c6 100644 --- a/clang/lib/DPCT/FileGenerator/GenFiles.cpp +++ b/clang/lib/DPCT/FileGenerator/GenFiles.cpp @@ -1209,7 +1209,7 @@ void loadYAMLIntoFileInfo(clang::tooling::UnifiedPath Path) { auto PreTU = std::make_shared(); if (fs::exists(YamlFilePath.getCanonicalPath())) { if (clang::dpct::DpctGlobalInfo::isIncMigration()) { - if (loadFromYaml(YamlFilePath, *PreTU) == 0) { + if (loadTUFromYaml(YamlFilePath, *PreTU) == 0) { DpctGlobalInfo::getInstance().insertReplInfoFromYAMLToFileInfo( OriginPath, std::move(PreTU)); } else { diff --git a/clang/lib/DPCT/IncMigration/ExternalReplacement.cpp b/clang/lib/DPCT/IncMigration/ExternalReplacement.cpp index ab365aa7e6ca..57a678a52ddc 100644 --- a/clang/lib/DPCT/IncMigration/ExternalReplacement.cpp +++ b/clang/lib/DPCT/IncMigration/ExternalReplacement.cpp @@ -75,8 +75,8 @@ int save2Yaml( return 0; } -int loadFromYaml(const clang::tooling::UnifiedPath &Input, - clang::tooling::TranslationUnitReplacements &TU) { +template +static int loadFromYaml(const clang::tooling::UnifiedPath &Input, T &Content) { llvm::ErrorOr> Buffer = llvm::MemoryBuffer::getFile(Input.getCanonicalPath()); if (!Buffer) { @@ -84,9 +84,20 @@ int loadFromYaml(const clang::tooling::UnifiedPath &Input, << Buffer.getError().message() << "\n"; return -1; } - llvm::yaml::Input YAMLIn(Buffer.get()->getBuffer()); - YAMLIn >> TU; + YAMLIn >> Content; + if (YAMLIn.error()) { + Content = T(); + return -1; + } + return 0; +} + +int loadTUFromYaml(const clang::tooling::UnifiedPath &Input, + clang::tooling::TranslationUnitReplacements &TU) { + int Status = loadFromYaml(Input, TU); + if (Status) + return Status; bool IsSrcFileChanged = false; for (const auto &digest : TU.MainSourceFilesDigest) { @@ -98,7 +109,7 @@ int loadFromYaml(const clang::tooling::UnifiedPath &Input, } } - if (IsSrcFileChanged || YAMLIn.error()) { + if (IsSrcFileChanged) { // File doesn't appear to be a header change description. Ignore it. TU = clang::tooling::TranslationUnitReplacements(); return -1; @@ -107,6 +118,11 @@ int loadFromYaml(const clang::tooling::UnifiedPath &Input, return 0; } +int loadGDCFromYaml(const clang::tooling::UnifiedPath &Input, + clang::tooling::GitDiffChanges &GDC) { + return loadFromYaml(Input, GDC); +} + void mergeAndUniqueReps( Replacements &Replaces, const std::vector &PreRepls) { diff --git a/clang/lib/DPCT/IncMigration/ExternalReplacement.h b/clang/lib/DPCT/IncMigration/ExternalReplacement.h index 39c27a378fdb..24e55d7c1ed0 100644 --- a/clang/lib/DPCT/IncMigration/ExternalReplacement.h +++ b/clang/lib/DPCT/IncMigration/ExternalReplacement.h @@ -33,8 +33,10 @@ namespace dpct { int mergeExternalReps(clang::tooling::UnifiedPath InRootSrcFilePath, clang::tooling::UnifiedPath OutRootSrcFilePath, clang::tooling::Replacements &Replaces); -int loadFromYaml(const clang::tooling::UnifiedPath &Input, - clang::tooling::TranslationUnitReplacements &TU); +int loadTUFromYaml(const clang::tooling::UnifiedPath &Input, + clang::tooling::TranslationUnitReplacements &TU); +int loadGDCFromYaml(const clang::tooling::UnifiedPath &Input, + clang::tooling::GitDiffChanges &GDC); int save2Yaml( clang::tooling::UnifiedPath &YamlFile, clang::tooling::UnifiedPath &SrcFileName, @@ -48,6 +50,7 @@ void mergeAndUniqueReps( clang::tooling::Replacements &Replaces, const std::vector &PreRepls); +int tryLoadingUpstreamChangesAndUserChanges(); } // namespace dpct } // namespace clang diff --git a/clang/lib/DPCT/IncMigration/IncrementalMigrationUtility.cpp b/clang/lib/DPCT/IncMigration/IncrementalMigrationUtility.cpp index 3f578a4f284a..a75e0c8ec9f4 100644 --- a/clang/lib/DPCT/IncMigration/IncrementalMigrationUtility.cpp +++ b/clang/lib/DPCT/IncMigration/IncrementalMigrationUtility.cpp @@ -376,7 +376,7 @@ bool canContinueMigration(std::string &Msg) { if (!llvm::sys::fs::exists(YamlFilePath.getCanonicalPath())) return true; - if (loadFromYaml(YamlFilePath.getCanonicalPath(), *PreTU) != 0) { + if (loadTUFromYaml(YamlFilePath.getCanonicalPath(), *PreTU) != 0) { llvm::errs() << getLoadYamlFailWarning(YamlFilePath.getCanonicalPath()); return true; } diff --git a/clang/lib/DPCT/IncMigration/ReMigratiom.cpp b/clang/lib/DPCT/IncMigration/ReMigratiom.cpp new file mode 100644 index 000000000000..883be5d6d9bc --- /dev/null +++ b/clang/lib/DPCT/IncMigration/ReMigratiom.cpp @@ -0,0 +1,28 @@ +//===----------------------- ReMigration.cpp ------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "ExternalReplacement.h" +#include "AnalysisInfo.h" + +using namespace clang::dpct; + +int tryLoadingUpstreamChangesAndUserChanges() { + llvm::SmallString<128> UpstreamChangesFilePath( + DpctGlobalInfo::getInRoot().getCanonicalPath()); + llvm::SmallString<128> UserChangesFilePath( + DpctGlobalInfo::getInRoot().getCanonicalPath()); + llvm::sys::path::append(UpstreamChangesFilePath, "UpstreamChanges.yaml"); + llvm::sys::path::append(UserChangesFilePath, "UserChanges.yaml"); + + clang::tooling::GitDiffChanges UpstreamChanges; + clang::tooling::GitDiffChanges UserChanges; + loadGDCFromYaml(UpstreamChangesFilePath, UpstreamChanges); + loadGDCFromYaml(UserChangesFilePath, UserChanges); + + return 0; +} diff --git a/clang/lib/DPCT/ReMigration/Hunk.cpp b/clang/lib/DPCT/ReMigration/Hunk.cpp deleted file mode 100644 index d36167b771ef..000000000000 --- a/clang/lib/DPCT/ReMigration/Hunk.cpp +++ /dev/null @@ -1,15 +0,0 @@ -//===--------------------------- Hunk.cpp ---------------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -#include "Hunk.h" - -namespace clang { -namespace dpct { -// TODO -} // namespace dpct -} // namespace clang diff --git a/clang/lib/DPCT/ReMigration/Hunk.h b/clang/lib/DPCT/ReMigration/Hunk.h deleted file mode 100644 index bd62c6cf7e0c..000000000000 --- a/clang/lib/DPCT/ReMigration/Hunk.h +++ /dev/null @@ -1,12 +0,0 @@ -//===--------------------------- Hunk.h -----------------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -#ifndef CLANG_DPCT_HUNK_H -#define CLANG_DPCT_HUNK_H - -#endif // CLANG_DPCT_HUNK_H From 63f6f8ecb80cece6629573b15f7d3775a5a51e10 Mon Sep 17 00:00:00 2001 From: "Jiang, Zhiwei" Date: Wed, 28 May 2025 14:46:52 +0800 Subject: [PATCH 11/53] Update Signed-off-by: Jiang, Zhiwei --- clang/tools/dpct/gitdiff2yaml/1.ref.txt | 14 +-- clang/tools/dpct/gitdiff2yaml/2.ref.txt | 8 +- clang/tools/dpct/gitdiff2yaml/3.ref.txt | 12 +- clang/tools/dpct/gitdiff2yaml/4.ref.txt | 56 +++++----- .../tools/dpct/gitdiff2yaml/gitdiff2yaml.cpp | 103 ++++++++++++++++-- 5 files changed, 132 insertions(+), 61 deletions(-) diff --git a/clang/tools/dpct/gitdiff2yaml/1.ref.txt b/clang/tools/dpct/gitdiff2yaml/1.ref.txt index e288c64de91c..23273daa15be 100644 --- a/clang/tools/dpct/gitdiff2yaml/1.ref.txt +++ b/clang/tools/dpct/gitdiff2yaml/1.ref.txt @@ -1,13 +1,5 @@ --- -Replacements: - - FilePath: '/dev/null' - Offset: 0 - Length: 0 - ReplacementText:"" - NewFilePath: 'b.txt' - - FilePath: '/dev/null' - Offset: 0 - Length: 0 - ReplacementText:"" - NewFilePath: 'c.txt' +AddFileHunks: + - NewFilePath: 'b.txt' + - NewFilePath: 'c.txt' ... diff --git a/clang/tools/dpct/gitdiff2yaml/2.ref.txt b/clang/tools/dpct/gitdiff2yaml/2.ref.txt index 20d377d8c0ca..6da1bae78ec3 100644 --- a/clang/tools/dpct/gitdiff2yaml/2.ref.txt +++ b/clang/tools/dpct/gitdiff2yaml/2.ref.txt @@ -1,8 +1,4 @@ --- -Replacements: - - FilePath: 'b.txt' - Offset: 0 - Length: 0 - ReplacementText:"" - NewFilePath: '/dev/null' +DeleteFileHunks: + - OldFilePath: 'b.txt' ... diff --git a/clang/tools/dpct/gitdiff2yaml/3.ref.txt b/clang/tools/dpct/gitdiff2yaml/3.ref.txt index 45125ba6d818..7579b9fee24b 100644 --- a/clang/tools/dpct/gitdiff2yaml/3.ref.txt +++ b/clang/tools/dpct/gitdiff2yaml/3.ref.txt @@ -1,8 +1,8 @@ --- -Replacements: - - FilePath: 'b.txt' - Offset: 12 - Length: 4 - ReplacementText:"777\n" - NewFilePath: 'c.txt' +MoveFileHunks: + - FilePath: 'b.txt' + Offset: 12 + Length: 4 + ReplacementText: "777\n" + NewFilePath: 'c.txt' ... diff --git a/clang/tools/dpct/gitdiff2yaml/4.ref.txt b/clang/tools/dpct/gitdiff2yaml/4.ref.txt index 7c53080ae731..58fe43dd35c5 100644 --- a/clang/tools/dpct/gitdiff2yaml/4.ref.txt +++ b/clang/tools/dpct/gitdiff2yaml/4.ref.txt @@ -1,33 +1,27 @@ --- -Replacements: - - FilePath: 'a.txt' - Offset: 8 - Length: 8 - ReplacementText:"111\n222\n" - NewFilePath: 'a.txt' - - FilePath: 'a.txt' - Offset: 20 - Length: 4 - ReplacementText:"333\n" - NewFilePath: 'a.txt' - - FilePath: 'a.txt' - Offset: 56 - Length: 0 - ReplacementText:"444\n" - NewFilePath: 'a.txt' - - FilePath: 'a.txt' - Offset: 92 - Length: 4 - ReplacementText:"" - NewFilePath: 'a.txt' - - FilePath: 'b.txt' - Offset: 40 - Length: 12 - ReplacementText:"" - NewFilePath: 'b.txt' - - FilePath: 'b.txt' - Offset: 92 - Length: 0 - ReplacementText:"111\n222\n333\n" - NewFilePath: 'b.txt' +ModifyFileHunks: + - FilePath: 'a.txt' + Offset: 8 + Length: 8 + ReplacementText: "111\n222\n" + - FilePath: 'a.txt' + Offset: 20 + Length: 4 + ReplacementText: "333\n" + - FilePath: 'a.txt' + Offset: 56 + Length: 0 + ReplacementText: "444\n" + - FilePath: 'a.txt' + Offset: 92 + Length: 4 + ReplacementText: "" + - FilePath: 'b.txt' + Offset: 40 + Length: 12 + ReplacementText: "" + - FilePath: 'b.txt' + Offset: 92 + Length: 0 + ReplacementText: "111\n222\n333\n" ... diff --git a/clang/tools/dpct/gitdiff2yaml/gitdiff2yaml.cpp b/clang/tools/dpct/gitdiff2yaml/gitdiff2yaml.cpp index c6501ae1d71d..4ed626d1d565 100644 --- a/clang/tools/dpct/gitdiff2yaml/gitdiff2yaml.cpp +++ b/clang/tools/dpct/gitdiff2yaml/gitdiff2yaml.cpp @@ -262,17 +262,106 @@ std::vector parseDiff(const std::string &diffOutput, return replacements; } +struct ModifyHunk { + std::string FilePath; + unsigned Offset = 0; + unsigned Length = 0; + std::string ReplacementText; +}; + +struct AddHunk { + std::string NewFilePath; +}; + +struct DeleteHunk { + std::string OldFilePath; +}; + +struct MoveHunk { + std::string FilePath; + unsigned Offset = 0; + unsigned Length = 0; + std::string ReplacementText; + std::string NewFilePath; +}; + void printYaml(std::ostream &stream, const std::vector &Repls) { - stream << "---" << std::endl; - stream << "Replacements:" << std::endl; + std::vector ModifyHunks; + std::vector AddHunks; + std::vector DeleteHunks; + std::vector MoveHunks; + for (const auto &R : Repls) { - stream << " - FilePath: " << "'" << R.OldFilePath << "'" + if (R.OldFilePath == "/dev/null" && R.NewFilePath != "/dev/null") { + // Add replacement + AddHunk AH; + AH.NewFilePath = R.NewFilePath; + AddHunks.push_back(AH); + continue; + } + if (R.OldFilePath != "/dev/null" && R.NewFilePath == "/dev/null") { + // Delete replacement + DeleteHunk DH; + DH.OldFilePath = R.OldFilePath; + DeleteHunks.push_back(DH); + continue; + } + if (R.OldFilePath == R.NewFilePath && R.OldFilePath != "/dev/null") { + // Modify replacement + ModifyHunk MH; + MH.FilePath = R.OldFilePath; + MH.Offset = R.Offset; + MH.Length = R.Length; + MH.ReplacementText = R.ReplacementText; + ModifyHunks.push_back(MH); + continue; + } + if (R.OldFilePath != R.NewFilePath) { + // Move replacement + MoveHunk MH; + MH.FilePath = R.OldFilePath; + MH.Offset = R.Offset; + MH.Length = R.Length; + MH.ReplacementText = R.ReplacementText; + MH.NewFilePath = R.NewFilePath; + MoveHunks.push_back(MH); + continue; + } + throw std::runtime_error("Invalid replacement: " + R.OldFilePath + " -> " + + R.NewFilePath); + } + + stream << "---" << std::endl; + if (!ModifyHunks.empty()) + stream << "ModifyFileHunks:" << std::endl; + for (const auto &H : ModifyHunks) { + stream << " - FilePath: " << "'" << H.FilePath << "'" << std::endl; + stream << " Offset: " << H.Offset << std::endl; + stream << " Length: " << H.Length << std::endl; + stream << " ReplacementText: " << "\"" << H.ReplacementText << "\"" + << std::endl; + } + if (!AddHunks.empty()) + stream << "AddFileHunks:" << std::endl; + for (const auto &H : AddHunks) { + stream << " - NewFilePath: " << "'" << H.NewFilePath << "'" + << std::endl; + } + if (!DeleteHunks.empty()) + stream << "DeleteFileHunks:" << std::endl; + for (const auto &H : DeleteHunks) { + stream << " - OldFilePath: " << "'" << H.OldFilePath << "'" << std::endl; - stream << " Offset: " << R.Offset << std::endl; - stream << " Length: " << R.Length << std::endl; - stream << " ReplacementText:" << "\"" << R.ReplacementText << "\"" + } + if (!MoveHunks.empty()) + stream << "MoveFileHunks:" << std::endl; + for (const auto &H : MoveHunks) { + stream << " - FilePath: " << "'" << H.FilePath << "'" << std::endl; + stream << " Offset: " << H.Offset << std::endl; + stream << " Length: " << H.Length << std::endl; + stream << " ReplacementText: " << "\"" << H.ReplacementText << "\"" << std::endl; - stream << " NewFilePath: " << "'" << R.NewFilePath << "'" + stream << " NewFilePath: " << "'" << H.NewFilePath << "'" << std::endl; } stream << "..." << std::endl; From 81ac3b60efb82c35bb962ca2e8bd029508eaa57e Mon Sep 17 00:00:00 2001 From: "Jiang, Zhiwei" Date: Wed, 28 May 2025 14:49:40 +0800 Subject: [PATCH 12/53] Fix Signed-off-by: Jiang, Zhiwei --- clang/lib/DPCT/IncMigration/{ReMigratiom.cpp => ReMigration.cpp} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename clang/lib/DPCT/IncMigration/{ReMigratiom.cpp => ReMigration.cpp} (100%) diff --git a/clang/lib/DPCT/IncMigration/ReMigratiom.cpp b/clang/lib/DPCT/IncMigration/ReMigration.cpp similarity index 100% rename from clang/lib/DPCT/IncMigration/ReMigratiom.cpp rename to clang/lib/DPCT/IncMigration/ReMigration.cpp From c1f18340d1fe22efdf9d7400a6e90f7200ae1ee3 Mon Sep 17 00:00:00 2001 From: "Jiang, Zhiwei" Date: Wed, 28 May 2025 16:34:39 +0800 Subject: [PATCH 13/53] Fix build Signed-off-by: Jiang, Zhiwei --- clang/lib/DPCT/IncMigration/ReMigration.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/clang/lib/DPCT/IncMigration/ReMigration.cpp b/clang/lib/DPCT/IncMigration/ReMigration.cpp index 883be5d6d9bc..25027507af43 100644 --- a/clang/lib/DPCT/IncMigration/ReMigration.cpp +++ b/clang/lib/DPCT/IncMigration/ReMigration.cpp @@ -6,10 +6,10 @@ // //===----------------------------------------------------------------------===// -#include "ExternalReplacement.h" #include "AnalysisInfo.h" +#include "ExternalReplacement.h" -using namespace clang::dpct; +namespace clang::dpct { int tryLoadingUpstreamChangesAndUserChanges() { llvm::SmallString<128> UpstreamChangesFilePath( @@ -26,3 +26,4 @@ int tryLoadingUpstreamChangesAndUserChanges() { return 0; } +} // namespace clang::dpct From ce62ba85e7a6e2e34b77bf826d84d2917076fe11 Mon Sep 17 00:00:00 2001 From: "Jiang, Zhiwei" Date: Thu, 29 May 2025 08:45:56 +0800 Subject: [PATCH 14/53] Update Signed-off-by: Jiang, Zhiwei --- clang/lib/DPCT/IncMigration/ExternalReplacement.cpp | 11 ++++++++--- clang/lib/DPCT/IncMigration/ExternalReplacement.h | 6 +++--- clang/lib/DPCT/IncMigration/ReMigration.cpp | 11 +++++++---- 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/clang/lib/DPCT/IncMigration/ExternalReplacement.cpp b/clang/lib/DPCT/IncMigration/ExternalReplacement.cpp index 57a678a52ddc..fe630a5a8f81 100644 --- a/clang/lib/DPCT/IncMigration/ExternalReplacement.cpp +++ b/clang/lib/DPCT/IncMigration/ExternalReplacement.cpp @@ -118,9 +118,14 @@ int loadTUFromYaml(const clang::tooling::UnifiedPath &Input, return 0; } -int loadGDCFromYaml(const clang::tooling::UnifiedPath &Input, - clang::tooling::GitDiffChanges &GDC) { - return loadFromYaml(Input, GDC); +void loadGDCFromYaml(const clang::tooling::UnifiedPath &Input, + clang::tooling::GitDiffChanges &GDC) { + int status = loadFromYaml(Input, GDC); + if (status) { + llvm::errs() << "Failed to load git diff Changes from " + << Input.getCanonicalPath() << "\n"; + GDC = clang::tooling::GitDiffChanges(); + } } void mergeAndUniqueReps( diff --git a/clang/lib/DPCT/IncMigration/ExternalReplacement.h b/clang/lib/DPCT/IncMigration/ExternalReplacement.h index 24e55d7c1ed0..e79c79e882f1 100644 --- a/clang/lib/DPCT/IncMigration/ExternalReplacement.h +++ b/clang/lib/DPCT/IncMigration/ExternalReplacement.h @@ -35,8 +35,8 @@ int mergeExternalReps(clang::tooling::UnifiedPath InRootSrcFilePath, clang::tooling::Replacements &Replaces); int loadTUFromYaml(const clang::tooling::UnifiedPath &Input, clang::tooling::TranslationUnitReplacements &TU); -int loadGDCFromYaml(const clang::tooling::UnifiedPath &Input, - clang::tooling::GitDiffChanges &GDC); +void loadGDCFromYaml(const clang::tooling::UnifiedPath &Input, + clang::tooling::GitDiffChanges &GDC); int save2Yaml( clang::tooling::UnifiedPath &YamlFile, clang::tooling::UnifiedPath &SrcFileName, @@ -50,7 +50,7 @@ void mergeAndUniqueReps( clang::tooling::Replacements &Replaces, const std::vector &PreRepls); -int tryLoadingUpstreamChangesAndUserChanges(); +void tryLoadingUpstreamChangesAndUserChanges(); } // namespace dpct } // namespace clang diff --git a/clang/lib/DPCT/IncMigration/ReMigration.cpp b/clang/lib/DPCT/IncMigration/ReMigration.cpp index 25027507af43..5238ee93c671 100644 --- a/clang/lib/DPCT/IncMigration/ReMigration.cpp +++ b/clang/lib/DPCT/IncMigration/ReMigration.cpp @@ -11,7 +11,7 @@ namespace clang::dpct { -int tryLoadingUpstreamChangesAndUserChanges() { +void tryLoadingUpstreamChangesAndUserChanges() { llvm::SmallString<128> UpstreamChangesFilePath( DpctGlobalInfo::getInRoot().getCanonicalPath()); llvm::SmallString<128> UserChangesFilePath( @@ -21,9 +21,12 @@ int tryLoadingUpstreamChangesAndUserChanges() { clang::tooling::GitDiffChanges UpstreamChanges; clang::tooling::GitDiffChanges UserChanges; - loadGDCFromYaml(UpstreamChangesFilePath, UpstreamChanges); - loadGDCFromYaml(UserChangesFilePath, UserChanges); - return 0; + if (llvm::sys::fs::exists(UpstreamChangesFilePath)) { + loadGDCFromYaml(UpstreamChangesFilePath, UpstreamChanges); + } + if (llvm::sys::fs::exists(UserChangesFilePath)) { + loadGDCFromYaml(UserChangesFilePath, UserChanges); + } } } // namespace clang::dpct From b4dc41d9edabad6d87eccb818491f21e97f3016b Mon Sep 17 00:00:00 2001 From: "Jiang, Zhiwei" Date: Thu, 29 May 2025 09:35:04 +0800 Subject: [PATCH 15/53] Save yaml into globalinfo Signed-off-by: Jiang, Zhiwei --- clang/lib/DPCT/AnalysisInfo.cpp | 3 +++ clang/lib/DPCT/AnalysisInfo.h | 8 ++++++++ clang/lib/DPCT/IncMigration/ReMigration.cpp | 8 +++----- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/clang/lib/DPCT/AnalysisInfo.cpp b/clang/lib/DPCT/AnalysisInfo.cpp index 8dc2d2dd7302..25cc18730ae4 100644 --- a/clang/lib/DPCT/AnalysisInfo.cpp +++ b/clang/lib/DPCT/AnalysisInfo.cpp @@ -2557,6 +2557,9 @@ bool DpctGlobalInfo::IsAfterBitsStdcxx = false; std::map> DpctGlobalInfo::AfterBitsStdcxxFiles; +clang::tooling::GitDiffChanges DpctGlobalInfo::UpstreamChanges; +clang::tooling::GitDiffChanges DpctGlobalInfo::UserChanges; + ///// class DpctNameGenerator ///// void DpctNameGenerator::printName(const FunctionDecl *FD, llvm::raw_ostream &OS) { diff --git a/clang/lib/DPCT/AnalysisInfo.h b/clang/lib/DPCT/AnalysisInfo.h index 6acc95f0b172..7adbd7ce1e04 100644 --- a/clang/lib/DPCT/AnalysisInfo.h +++ b/clang/lib/DPCT/AnalysisInfo.h @@ -1405,6 +1405,12 @@ class DpctGlobalInfo { getAfterBitsStdcxxFilesMap() { return AfterBitsStdcxxFiles; } + static clang::tooling::GitDiffChanges &getUpstreamChanges() { + return UpstreamChanges; + } + static clang::tooling::GitDiffChanges &getUserChanges() { + return UserChanges; + } std::shared_ptr insertFile(const clang::tooling::UnifiedPath &FilePath) { return insertObject(FileMap, FilePath); @@ -1747,6 +1753,8 @@ class DpctGlobalInfo { static std::map> AfterBitsStdcxxFiles; + static clang::tooling::GitDiffChanges UpstreamChanges; + static clang::tooling::GitDiffChanges UserChanges; }; /// Generate mangle name of FunctionDecl as key of DeviceFunctionInfo. diff --git a/clang/lib/DPCT/IncMigration/ReMigration.cpp b/clang/lib/DPCT/IncMigration/ReMigration.cpp index 5238ee93c671..3ea2d37cf7a0 100644 --- a/clang/lib/DPCT/IncMigration/ReMigration.cpp +++ b/clang/lib/DPCT/IncMigration/ReMigration.cpp @@ -19,14 +19,12 @@ void tryLoadingUpstreamChangesAndUserChanges() { llvm::sys::path::append(UpstreamChangesFilePath, "UpstreamChanges.yaml"); llvm::sys::path::append(UserChangesFilePath, "UserChanges.yaml"); - clang::tooling::GitDiffChanges UpstreamChanges; - clang::tooling::GitDiffChanges UserChanges; - if (llvm::sys::fs::exists(UpstreamChangesFilePath)) { - loadGDCFromYaml(UpstreamChangesFilePath, UpstreamChanges); + loadGDCFromYaml(UpstreamChangesFilePath, + DpctGlobalInfo::getUpstreamChanges()); } if (llvm::sys::fs::exists(UserChangesFilePath)) { - loadGDCFromYaml(UserChangesFilePath, UserChanges); + loadGDCFromYaml(UserChangesFilePath, DpctGlobalInfo::getUserChanges()); } } } // namespace clang::dpct From 94b726822e5f3a2ed9761b58d0526bd4cc93ae7e Mon Sep 17 00:00:00 2001 From: "Jiang, Zhiwei" Date: Thu, 29 May 2025 10:53:37 +0800 Subject: [PATCH 16/53] Fix path Signed-off-by: Jiang, Zhiwei --- .../include/clang/Tooling/Core/Replacement.h | 6 ++--- clang/lib/DPCT/IncMigration/ReMigration.cpp | 27 +++++++++++++++++++ clang/lib/Tooling/Core/Replacement.cpp | 6 +++++ 3 files changed, 35 insertions(+), 4 deletions(-) diff --git a/clang/include/clang/Tooling/Core/Replacement.h b/clang/include/clang/Tooling/Core/Replacement.h index 37e1d8934725..5f00753652d1 100644 --- a/clang/include/clang/Tooling/Core/Replacement.h +++ b/clang/include/clang/Tooling/Core/Replacement.h @@ -478,8 +478,7 @@ class AddFileHunk : public Hunk { public: AddFileHunk() : Hunk(AddFile) {} - AddFileHunk(std::string NewFilePath) - : Hunk(AddFile), NewFilePath(std::move(NewFilePath)) {} + AddFileHunk(std::string NewFilePath); const std::string &getNewFilePath() const { return NewFilePath; } }; @@ -488,8 +487,7 @@ class DeleteFileHunk : public Hunk { public: DeleteFileHunk() : Hunk(DeleteFile) {} - DeleteFileHunk(std::string OldFilePath) - : Hunk(DeleteFile), OldFilePath(std::move(OldFilePath)) {} + DeleteFileHunk(std::string OldFilePath); const std::string &getOldFilePath() const { return OldFilePath; } }; diff --git a/clang/lib/DPCT/IncMigration/ReMigration.cpp b/clang/lib/DPCT/IncMigration/ReMigration.cpp index 3ea2d37cf7a0..d6fe684e014c 100644 --- a/clang/lib/DPCT/IncMigration/ReMigration.cpp +++ b/clang/lib/DPCT/IncMigration/ReMigration.cpp @@ -11,6 +11,33 @@ namespace clang::dpct { +static void dumpGitDiffChanges(const clang::tooling::GitDiffChanges& GHC) { + llvm::errs() << "GitDiffChanges:\n"; + llvm::errs() << " ModifyFileHunks:\n"; + for (const auto &Hunk : GHC.ModifyFileHunks) { + llvm::errs() << " - FilePath: " << Hunk.getFilePath() << "\n"; + llvm::errs() << " Offset: " << Hunk.getOffset() << "\n"; + llvm::errs() << " Length: " << Hunk.getLength() << "\n"; + llvm::errs() << " ReplacementText: " << Hunk.getReplacementText() << "\n"; + } + llvm::errs() << " AddFileHunks:\n"; + for (const auto &Hunk : GHC.AddFileHunks) { + llvm::errs() << " - NewFilePath: " << Hunk.getNewFilePath() << "\n"; + } + llvm::errs() << " DeleteFileHunks:\n"; + for (const auto &Hunk : GHC.DeleteFileHunks) { + llvm::errs() << " - OldFilePath: " << Hunk.getOldFilePath() << "\n"; + } + llvm::errs() << " MoveFileHunks:\n"; + for (const auto &Hunk : GHC.MoveFileHunks) { + llvm::errs() << " - FilePath: " << Hunk.getFilePath() << "\n"; + llvm::errs() << " Offset: " << Hunk.getOffset() << "\n"; + llvm::errs() << " Length: " << Hunk.getLength() << "\n"; + llvm::errs() << " ReplacementText: " << Hunk.getReplacementText() << "\n"; + llvm::errs() << " NewFilePath: " << Hunk.getNewFilePath() << "\n"; + } +} + void tryLoadingUpstreamChangesAndUserChanges() { llvm::SmallString<128> UpstreamChangesFilePath( DpctGlobalInfo::getInRoot().getCanonicalPath()); diff --git a/clang/lib/Tooling/Core/Replacement.cpp b/clang/lib/Tooling/Core/Replacement.cpp index 19e96a2905b7..8e927de715d3 100644 --- a/clang/lib/Tooling/Core/Replacement.cpp +++ b/clang/lib/Tooling/Core/Replacement.cpp @@ -727,6 +727,12 @@ std::map groupReplacementsByFile( return Result; } #ifdef SYCLomatic_CUSTOMIZATION +AddFileHunk::AddFileHunk(std::string NewFilePath) + : Hunk(AddFile), NewFilePath(UnifiedPath(NewFilePath).getCanonicalPath()) {} +DeleteFileHunk::DeleteFileHunk(std::string OldFilePath) + : Hunk(DeleteFile), + OldFilePath(UnifiedPath(OldFilePath).getCanonicalPath()) {} + #undef Replacement #endif // SYCLomatic_CUSTOMIZATION From f31055b2fc752ec560c7e305e4eb8863c5a878ec Mon Sep 17 00:00:00 2001 From: "Jiang, Zhiwei" Date: Thu, 29 May 2025 11:14:20 +0800 Subject: [PATCH 17/53] Format Signed-off-by: Jiang, Zhiwei --- clang/lib/DPCT/IncMigration/ReMigration.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/clang/lib/DPCT/IncMigration/ReMigration.cpp b/clang/lib/DPCT/IncMigration/ReMigration.cpp index d6fe684e014c..192615b0d22f 100644 --- a/clang/lib/DPCT/IncMigration/ReMigration.cpp +++ b/clang/lib/DPCT/IncMigration/ReMigration.cpp @@ -11,14 +11,15 @@ namespace clang::dpct { -static void dumpGitDiffChanges(const clang::tooling::GitDiffChanges& GHC) { +static void dumpGitDiffChanges(const clang::tooling::GitDiffChanges &GHC) { llvm::errs() << "GitDiffChanges:\n"; llvm::errs() << " ModifyFileHunks:\n"; for (const auto &Hunk : GHC.ModifyFileHunks) { llvm::errs() << " - FilePath: " << Hunk.getFilePath() << "\n"; llvm::errs() << " Offset: " << Hunk.getOffset() << "\n"; llvm::errs() << " Length: " << Hunk.getLength() << "\n"; - llvm::errs() << " ReplacementText: " << Hunk.getReplacementText() << "\n"; + llvm::errs() << " ReplacementText: " << Hunk.getReplacementText() + << "\n"; } llvm::errs() << " AddFileHunks:\n"; for (const auto &Hunk : GHC.AddFileHunks) { @@ -33,7 +34,8 @@ static void dumpGitDiffChanges(const clang::tooling::GitDiffChanges& GHC) { llvm::errs() << " - FilePath: " << Hunk.getFilePath() << "\n"; llvm::errs() << " Offset: " << Hunk.getOffset() << "\n"; llvm::errs() << " Length: " << Hunk.getLength() << "\n"; - llvm::errs() << " ReplacementText: " << Hunk.getReplacementText() << "\n"; + llvm::errs() << " ReplacementText: " << Hunk.getReplacementText() + << "\n"; llvm::errs() << " NewFilePath: " << Hunk.getNewFilePath() << "\n"; } } From 63e6fbc6dd2275c12c41f6647fd4c1720c99391d Mon Sep 17 00:00:00 2001 From: "Jiang, Zhiwei" Date: Thu, 29 May 2025 11:17:54 +0800 Subject: [PATCH 18/53] Format Signed-off-by: Jiang, Zhiwei --- clang/tools/dpct/gitdiff2yaml/gitdiff2yaml.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clang/tools/dpct/gitdiff2yaml/gitdiff2yaml.cpp b/clang/tools/dpct/gitdiff2yaml/gitdiff2yaml.cpp index 4ed626d1d565..6228182237f7 100644 --- a/clang/tools/dpct/gitdiff2yaml/gitdiff2yaml.cpp +++ b/clang/tools/dpct/gitdiff2yaml/gitdiff2yaml.cpp @@ -315,7 +315,7 @@ void printYaml(std::ostream &stream, const std::vector &Repls) { MH.ReplacementText = R.ReplacementText; ModifyHunks.push_back(MH); continue; - } + } if (R.OldFilePath != R.NewFilePath) { // Move replacement MoveHunk MH; @@ -328,7 +328,7 @@ void printYaml(std::ostream &stream, const std::vector &Repls) { continue; } throw std::runtime_error("Invalid replacement: " + R.OldFilePath + " -> " + - R.NewFilePath); + R.NewFilePath); } stream << "---" << std::endl; From 89edb93dc8bdf0b3c472e799eb88f03c1250d974 Mon Sep 17 00:00:00 2001 From: "Jiang, Zhiwei" Date: Tue, 3 Jun 2025 10:40:07 +0800 Subject: [PATCH 19/53] WIP Signed-off-by: Jiang, Zhiwei --- clang/lib/DPCT/IncMigration/ReMigration.cpp | 176 ++++++++++++++++++++ 1 file changed, 176 insertions(+) diff --git a/clang/lib/DPCT/IncMigration/ReMigration.cpp b/clang/lib/DPCT/IncMigration/ReMigration.cpp index 192615b0d22f..1a26674c8ece 100644 --- a/clang/lib/DPCT/IncMigration/ReMigration.cpp +++ b/clang/lib/DPCT/IncMigration/ReMigration.cpp @@ -8,6 +8,7 @@ #include "AnalysisInfo.h" #include "ExternalReplacement.h" +#include "clang/Tooling/Core/Replacement.h" namespace clang::dpct { @@ -56,4 +57,179 @@ void tryLoadingUpstreamChangesAndUserChanges() { loadGDCFromYaml(UserChangesFilePath, DpctGlobalInfo::getUserChanges()); } } + +/// Calculate the new Repls of the input \p NewRepl after \p Repls is applied to +/// the files. +/// \param Repls Replacements to apply. +/// \param NewRepl Replacements before applying \p Repls. +/// \return The result Repls. +clang::tooling::Replacements +calculateUpdatedRanges(const clang::tooling::Replacements &Repls, + const clang::tooling::Replacements &NewRepl) { + clang::tooling::Replacements Result; + for (const auto &R : NewRepl) { + unsigned int BOffset = Repls.getShiftedCodePosition(R.getOffset()); + unsigned int EOffset = + Repls.getShiftedCodePosition(R.getOffset() + R.getLength()); + if (BOffset > EOffset) + continue; + (void)Result.add(clang::tooling::DpctReplacement( + R.getFilePath(), BOffset, EOffset - BOffset, R.getReplacementText())); + } + return Result; +} + +std::map> +groupReplcementsFileByFile( + const std::vector &Repls) { + std::map> Result; + for (const auto &R : Repls) { + Result[R.getFilePath().str()].push_back(R); + } + return Result; +} + +// Repl A +// [CUDA code 1] -----------------------------------------> [CUDA code 2] +// | / | +// | Repl C1 / | Repl B +// | ________/ | +// V / V +// [SYCL code 1] / [SYCL code 2] +// | / | +// | Repl C2 Repl D / | +// | / | +// V shift V merge | +// [SYCL code 1.1] ----------------> [SYCL code 1.1] ----------> | +// (based on CUDA code 1) (based on CUDA code 2) | +// V +// [SYCL code 2.1] +// +// Repl_A: Read from gitdiff2yaml generated files. +// Repl_B: Curent in-memory migration replacements. +// Repl C1: Read from MainSourceFiles.yaml (and *.h.yaml) file(s). +// Repl_C2: Read from gitdiff2yaml generated files. +// +// Repl_A has 4 parts: +// Repl_A_1: New added files. +// Repl_A_2: Replacements in modified files. +// Repl_A_3: Deleted files. +// Repl_A_4: Replacements in moved files. +// +// Merge process: +// 1. Merge Repl_C1 and Repl_C2 directly, named Repl_C. There is no conlict. +// Repl_C can be divided in to 2 parts: +// Repl_C_x: Replacements which in ranges of Repl_A_3 or delete hunks in +// Repl_A_2/Repl_A_4. +// Repl_C_y: Other replacements. +// Repl_C_x will be ignored during this merge. +// 2. Shfit Repl_C_y with Repl_A, called Repl_D. +// 3. Merge Repl_D and Repl_B. May have conflicts. +std::map +reMigrationMerge(const clang::tooling::GitDiffChanges &Repl_A, + const std::vector &Repl_B, + const std::vector &Repl_C1, + const clang::tooling::GitDiffChanges &Repl_C2) { + assert(Repl_C2.AddFileHunks.empty() && Repl_C2.DeleteFileHunks.empty() && + Repl_C2.MoveFileHunks.empty() && + "Repl_C2 should only have ModifiyFileHunks."); + std::vector Repl_C; + // Merge Repl_C1 and Repl_C2 + Repl_C.insert(Repl_C.end(), Repl_C1.begin(), Repl_C1.end()); + for (const auto &Hunk : Repl_C2.ModifyFileHunks) { + clang::tooling::DpctReplacement Replacement( + Hunk.getFilePath(), Hunk.getOffset(), Hunk.getLength(), + Hunk.getReplacementText()); + Repl_C.push_back(Replacement); + } + + // Convert vector in Repl_A to map for quick lookup. + std::map> + DeletedParts; + std::map ModifiedParts; + for (const auto &Hunk : Repl_A.ModifyFileHunks) { + if (Hunk.getLength() != 0 && Hunk.getReplacementText().size() == 0) { + DeletedParts[Hunk.getFilePath().str()][Hunk.getOffset()] = + Hunk.getLength(); + } + (void)ModifiedParts[Hunk.getFilePath().str()].add( + clang::tooling::DpctReplacement(Hunk.getFilePath().str(), + Hunk.getOffset(), Hunk.getLength(), + Hunk.getReplacementText())); + } + for (const auto &Hunk : Repl_A.MoveFileHunks) { + if (Hunk.getLength() != 0 && Hunk.getReplacementText().size() == 0) { + DeletedParts[Hunk.getFilePath().str()][Hunk.getOffset()] = Hunk.getLength(); + } + (void)ModifiedParts[Hunk.getFilePath().str()].add( + clang::tooling::DpctReplacement(Hunk.getFilePath().str(), + Hunk.getOffset(), Hunk.getLength(), + Hunk.getReplacementText())); + } + for (const auto &Hunk : Repl_A.DeleteFileHunks) { + DeletedParts[Hunk.getOldFilePath()] = std::map(); + } + + // Get Repl_C_y + std::map Repl_C_y; + for (const auto &Repl : Repl_B) { + // The gitdiff changes are line-based while clang replacements are character-based. + // So here assume there is no overlap between delete hunks and replacements. + const auto &It = DeletedParts.find(Repl.getFilePath().str()); + if (It == DeletedParts.end()) { + (void)Repl_C_y[Repl.getFilePath().str()].add( + clang::tooling::DpctReplacement(Repl.getFilePath().str(), + Repl.getOffset(), Repl.getLength(), + Repl.getReplacementText())); + continue; + } + + // Check if the replacement is in a deleted part. + // TODO: Use Interval Tree to speed up the lookup. + for (const auto &Part : It->second) { + if (Repl.getOffset() >= Part.first && + Repl.getOffset() + Repl.getLength() <= Part.first + Part.second) { + break; + } + } + (void)Repl_C_y[Repl.getFilePath().str()].add( + clang::tooling::DpctReplacement(Repl.getFilePath().str(), + Repl.getOffset(), Repl.getLength(), + Repl.getReplacementText())); + } + + // Shift Repl_C_y with Repl_A(ModifiedParts) + std::map Repl_D; + for (const auto& Item: Repl_C_y) { + const auto &FilePath = Item.first; + const auto &Repls = Item.second; + + // Check if the file has modified parts. + const auto &It = ModifiedParts.find(FilePath); + if (It == ModifiedParts.end()) { + Repl_D[FilePath] = Repls; + continue; + } + + Repl_D[FilePath] = calculateUpdatedRanges(It->second, Repls); + } + + // Group Repl_B by file + const auto Repl_B_by_file = groupReplcementsFileByFile(Repl_B); + // Merge Repl_D and Repl_B + // 1. we need group the replacements by line number fisrt. For repl in the same line, we need merge them together. + // 2. then we should convert the replacements to a map . We will have 2 maps. + // 3. we need also get a map for current file (CUDA code 2) + // 4. merge by line, generate a new map + // 5. convert that map to Replacements. + for (const auto &Repls : Repl_B_by_file) { + + + } + + + + std::map Result; + return Result; +} } // namespace clang::dpct From 8eb1e47489d0d168ab354e49d32ca48506cf0eb4 Mon Sep 17 00:00:00 2001 From: "Jiang, Zhiwei" Date: Wed, 4 Jun 2025 08:22:54 +0800 Subject: [PATCH 20/53] 1 Signed-off-by: Jiang, Zhiwei --- clang/lib/DPCT/IncMigration/ReMigration.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/clang/lib/DPCT/IncMigration/ReMigration.cpp b/clang/lib/DPCT/IncMigration/ReMigration.cpp index 1a26674c8ece..0236de753a6c 100644 --- a/clang/lib/DPCT/IncMigration/ReMigration.cpp +++ b/clang/lib/DPCT/IncMigration/ReMigration.cpp @@ -9,6 +9,7 @@ #include "AnalysisInfo.h" #include "ExternalReplacement.h" #include "clang/Tooling/Core/Replacement.h" +#include "llvm/Support/raw_ostream.h" namespace clang::dpct { @@ -219,7 +220,7 @@ reMigrationMerge(const clang::tooling::GitDiffChanges &Repl_A, // Merge Repl_D and Repl_B // 1. we need group the replacements by line number fisrt. For repl in the same line, we need merge them together. // 2. then we should convert the replacements to a map . We will have 2 maps. - // 3. we need also get a map for current file (CUDA code 2) + // 3. we need a vector for current file (CUDA code 2) // 4. merge by line, generate a new map // 5. convert that map to Replacements. for (const auto &Repls : Repl_B_by_file) { From c4397d04bd76b57d064bc74a87945d42f8b970cc Mon Sep 17 00:00:00 2001 From: "Jiang, Zhiwei" Date: Wed, 4 Jun 2025 09:01:28 +0800 Subject: [PATCH 21/53] remove unused changes Signed-off-by: Jiang, Zhiwei --- .../include/clang/Tooling/Core/Replacement.h | 55 ++++-------- .../include/clang/Tooling/ReplacementsYaml.h | 88 ++++++------------- clang/lib/DPCT/AnalysisInfo.cpp | 2 +- clang/lib/DPCT/AnalysisInfo.h | 6 +- clang/lib/DPCT/FileGenerator/GenFiles.cpp | 8 +- .../DPCT/IncMigration/ExternalReplacement.cpp | 8 +- .../DPCT/IncMigration/ExternalReplacement.h | 4 +- clang/lib/DPCT/RulesLang/RulesLang.h | 6 +- .../RulesLang/RulesLangNoneAPIAndType.cpp | 13 ++- clang/lib/DPCT/TextModification.h | 14 +-- clang/lib/Tooling/Core/Replacement.cpp | 5 -- 11 files changed, 78 insertions(+), 131 deletions(-) diff --git a/clang/include/clang/Tooling/Core/Replacement.h b/clang/include/clang/Tooling/Core/Replacement.h index 5f00753652d1..d9ce05c59c62 100644 --- a/clang/include/clang/Tooling/Core/Replacement.h +++ b/clang/include/clang/Tooling/Core/Replacement.h @@ -54,7 +54,7 @@ enum class ConstantFlagType : int { HostDeviceInOnePass = 4 }; enum class HelperFileEnum : unsigned int; -} // namespace dpct +} #endif // SYCLomatic_CUSTOMIZATION namespace tooling { @@ -139,6 +139,9 @@ class Replacement { unsigned getOffset() const { return ReplacementRange.getOffset(); } unsigned getLength() const { return ReplacementRange.getLength(); } StringRef getReplacementText() const { return ReplacementText; } +#ifdef SYCLomatic_CUSTOMIZATION + void setReplacementText(const std::string Str) { ReplacementText = Str; } +#endif // SYCLomatic_CUSTOMIZATION /// @} /// Applies the replacement on the Rewriter. @@ -146,35 +149,8 @@ class Replacement { /// Returns a human readable string representation. std::string toString() const; - -#ifdef SYCLomatic_CUSTOMIZATION -protected: -#else -private: -#endif // SYCLomatic_CUSTOMIZATION - void setFromSourceLocation(const SourceManager &Sources, SourceLocation Start, - unsigned Length, StringRef ReplacementText); - void setFromSourceRange(const SourceManager &Sources, - const CharSourceRange &Range, - StringRef ReplacementText, - const LangOptions &LangOpts); - - std::string FilePath; - Range ReplacementRange; - std::string ReplacementText; -}; - #ifdef SYCLomatic_CUSTOMIZATION -class DpctReplacement : public Replacement { -public: - using Replacement::Replacement; - DpctReplacement(const Replacement &R) : Replacement(R) {} - - void setReplacementText(const std::string Str) { ReplacementText = Str; } - - void setBlockLevelFormatFlag(bool Flag = true) { - BlockLevelFormatFlag = Flag; - } + void setBlockLevelFormatFlag(bool Flag = true) { BlockLevelFormatFlag = Flag; } bool getBlockLevelFormatFlag() const { return BlockLevelFormatFlag; } void setNotFormatFlag() { NotFormatFlag = true; } bool getNotFormatFlag() const { return NotFormatFlag; } @@ -189,8 +165,20 @@ class DpctReplacement : public Replacement { void setOffset(unsigned int Offset) { ReplacementRange = Range(Offset, ReplacementRange.getLength()); } +#endif // SYCLomatic_CUSTOMIZATION private: + void setFromSourceLocation(const SourceManager &Sources, SourceLocation Start, + unsigned Length, StringRef ReplacementText); + void setFromSourceRange(const SourceManager &Sources, + const CharSourceRange &Range, + StringRef ReplacementText, + const LangOptions &LangOpts); + + std::string FilePath; + Range ReplacementRange; + std::string ReplacementText; +#ifdef SYCLomatic_CUSTOMIZATION bool BlockLevelFormatFlag = false; bool NotFormatFlag = false; // Record the __constant__ variable is used in host, device or hostdevice @@ -205,8 +193,8 @@ class DpctReplacement : public Replacement { // appending "_host_ct1". Since the original name can only be collected when // it used in device code, so we need record it. std::string NewHostVarName = ""; -}; #endif // SYCLomatic_CUSTOMIZATION +}; enum class replacement_error { fail_to_apply = 0, @@ -277,9 +265,6 @@ inline bool operator!=(const Replacement &LHS, const Replacement &RHS) { /// offset (i.e. order-dependent). class Replacements { private: -#ifdef SYCLomatic_CUSTOMIZATION - using Replacement = DpctReplacement; -#endif // SYCLomatic_CUSTOMIZATION using ReplacementsImpl = std::set; public: @@ -530,10 +515,8 @@ struct TranslationUnitReplacements { // Keep here only for backward compatibility - end std::map> CompileTargets; std::map OptionMap; - std::vector Replacements; -#else - std::vector Replacements; #endif // SYCLomatic_CUSTOMIZATION + std::vector Replacements; }; /// Calculates the new ranges after \p Replaces are applied. These diff --git a/clang/include/clang/Tooling/ReplacementsYaml.h b/clang/include/clang/Tooling/ReplacementsYaml.h index da7e2e45db4c..c8fcf5cf0725 100644 --- a/clang/include/clang/Tooling/ReplacementsYaml.h +++ b/clang/include/clang/Tooling/ReplacementsYaml.h @@ -14,11 +14,9 @@ #ifndef LLVM_CLANG_TOOLING_REPLACEMENTSYAML_H #define LLVM_CLANG_TOOLING_REPLACEMENTSYAML_H - #include "clang/Tooling/Refactoring.h" #include "llvm/Support/YAMLTraits.h" #include - LLVM_YAML_IS_SEQUENCE_VECTOR(clang::tooling::Replacement) #ifdef SYCLomatic_CUSTOMIZATION // Keep here only for backward compatibility - begin @@ -31,7 +29,6 @@ LLVM_YAML_IS_STRING_MAP(std::vector) LLVM_YAML_IS_SEQUENCE_VECTOR(clang::tooling::MainSourceFileInfo) LLVM_YAML_IS_STRING_MAP(std::vector) LLVM_YAML_IS_STRING_MAP(clang::tooling::OptionInfo) -LLVM_YAML_IS_SEQUENCE_VECTOR(clang::tooling::DpctReplacement) LLVM_YAML_IS_SEQUENCE_VECTOR(clang::tooling::ModifyFileHunk) LLVM_YAML_IS_SEQUENCE_VECTOR(clang::tooling::AddFileHunk) LLVM_YAML_IS_SEQUENCE_VECTOR(clang::tooling::DeleteFileHunk) @@ -55,49 +52,20 @@ ScalarEnumerationTraits::enumeration( namespace llvm { namespace yaml { - /// Specialized MappingTraits to describe how a Replacement is /// (de)serialized. template <> struct MappingTraits { /// Helper to (de)serialize a Replacement since we don't have direct /// access to its data members. struct NormalizedReplacement { - NormalizedReplacement(const IO &) : Offset(0), Length(0) {} - - NormalizedReplacement(const IO &, const clang::tooling::Replacement &R) - : FilePath(R.getFilePath()), Offset(R.getOffset()), - Length(R.getLength()), ReplacementText(R.getReplacementText()) {} - - clang::tooling::Replacement denormalize(const IO &) { - return clang::tooling::Replacement(FilePath, Offset, Length, - ReplacementText); +#ifdef SYCLomatic_CUSTOMIZATION + NormalizedReplacement(const IO &) + : FilePath(""), Offset(0), Length(0), ReplacementText(""), + ConstantFlag(""), ConstantOffset(0), InitStr(""), NewHostVarName(""), + BlockLevelFormatFlag(false) { } - std::string FilePath; - unsigned int Offset; - unsigned int Length; - std::string ReplacementText; - }; - - static void mapping(IO &Io, clang::tooling::Replacement &R) { - MappingNormalization - Keys(Io, R); - Io.mapRequired("FilePath", Keys->FilePath); - Io.mapRequired("Offset", Keys->Offset); - Io.mapRequired("Length", Keys->Length); - Io.mapRequired("ReplacementText", Keys->ReplacementText); - } -}; - -#ifdef SYCLomatic_CUSTOMIZATION -template <> struct MappingTraits { - struct NormalizedDpctReplacement { - NormalizedDpctReplacement(const IO &io) - : Offset(0), Length(0), ConstantFlag(""), ConstantOffset(0), - InitStr(""), NewHostVarName(""), BlockLevelFormatFlag(false) {} - - NormalizedDpctReplacement(const IO &io, - const clang::tooling::DpctReplacement &R) + NormalizedReplacement(const IO &, const clang::tooling::Replacement &R) : FilePath(R.getFilePath()), Offset(R.getOffset()), Length(R.getLength()), ReplacementText(R.getReplacementText()), ConstantOffset(R.getConstantOffset()), InitStr(R.getInitStr()), @@ -115,9 +83,9 @@ template <> struct MappingTraits { } } - clang::tooling::DpctReplacement denormalize(const IO &) { - clang::tooling::DpctReplacement R(FilePath, Offset, Length, - ReplacementText); + clang::tooling::Replacement denormalize(const IO &) { + auto R = clang::tooling::Replacement(FilePath, Offset, Length, + ReplacementText); if (ConstantFlag == "HostDeviceConstant") { R.setConstantFlag(clang::dpct::ConstantFlagType::HostDevice); } else if (ConstantFlag == "DeviceConstant") { @@ -133,55 +101,67 @@ template <> struct MappingTraits { R.setBlockLevelFormatFlag(BlockLevelFormatFlag); return R; } +#else + NormalizedReplacement(const IO &) : Offset(0), Length(0) {} + + NormalizedReplacement(const IO &, const clang::tooling::Replacement &R) + : FilePath(R.getFilePath()), Offset(R.getOffset()), + Length(R.getLength()), ReplacementText(R.getReplacementText()) {} + + clang::tooling::Replacement denormalize(const IO &) { + return clang::tooling::Replacement(FilePath, Offset, Length, + ReplacementText); + } +#endif // SYCLomatic_CUSTOMIZATION std::string FilePath; unsigned int Offset; unsigned int Length; std::string ReplacementText; +#ifdef SYCLomatic_CUSTOMIZATION std::string ConstantFlag = ""; unsigned int ConstantOffset = 0; std::string InitStr = ""; std::string NewHostVarName = ""; bool BlockLevelFormatFlag = false; +#endif // SYCLomatic_CUSTOMIZATION }; - static void mapping(IO &Io, clang::tooling::DpctReplacement &R) { - MappingNormalization - Keys(Io, R); + static void mapping(IO &Io, clang::tooling::Replacement &R) { + MappingNormalization + Keys(Io, R); Io.mapRequired("FilePath", Keys->FilePath); Io.mapRequired("Offset", Keys->Offset); Io.mapRequired("Length", Keys->Length); Io.mapRequired("ReplacementText", Keys->ReplacementText); +#ifdef SYCLomatic_CUSTOMIZATION Io.mapOptional("ConstantFlag", Keys->ConstantFlag); Io.mapOptional("ConstantOffset", Keys->ConstantOffset); Io.mapOptional("InitStr", Keys->InitStr); Io.mapOptional("NewHostVarName", Keys->NewHostVarName); Io.mapOptional("BlockLevelFormatFlag", Keys->BlockLevelFormatFlag); +#endif // SYCLomatic_CUSTOMIZATION } }; +#ifdef SYCLomatic_CUSTOMIZATION template <> struct MappingTraits { struct NormalizedMainSourceFilesDigest { NormalizedMainSourceFilesDigest(const IO &) : MainSourceFile(""), Digest(""), HasCUDASyntax(false) {} - NormalizedMainSourceFilesDigest(const IO &, clang::tooling::MainSourceFileInfo &R) : MainSourceFile(R.MainSourceFile), Digest(R.Digest), HasCUDASyntax(R.HasCUDASyntax) {} - clang::tooling::MainSourceFileInfo denormalize(const IO &) { return clang::tooling::MainSourceFileInfo(MainSourceFile, Digest, HasCUDASyntax); } - std::string MainSourceFile = ""; std::string Digest = ""; bool HasCUDASyntax = false; }; - static void mapping(IO &Io, clang::tooling::MainSourceFileInfo &R) { MappingNormalization @@ -191,18 +171,14 @@ template <> struct MappingTraits { Io.mapOptional("HasCUDASyntax", Keys->HasCUDASyntax); } }; - template <> struct MappingTraits { struct NormalizedCompileCmds { - NormalizedCompileCmds(const IO &) : MigratedFileName(""), CompileOptions(""), Compiler(""){} - NormalizedCompileCmds(const IO &, clang::tooling::CompilationInfo &CmpInfo) : MigratedFileName(CmpInfo.MigratedFileName), CompileOptions(CmpInfo.CompileOptions), Compiler(CmpInfo.Compiler) {} - clang::tooling::CompilationInfo denormalize(const IO &) { clang::tooling::CompilationInfo CmpInfo; CmpInfo.MigratedFileName = MigratedFileName; @@ -210,12 +186,10 @@ template <> struct MappingTraits { CmpInfo.Compiler = Compiler; return CmpInfo; } - std::string MigratedFileName; std::string CompileOptions; std::string Compiler; }; - static void mapping(IO &Io, clang::tooling::CompilationInfo &CmpInfo) { MappingNormalization Keys(Io, CmpInfo); @@ -224,14 +198,12 @@ template <> struct MappingTraits { Io.mapOptional("Compiler", Keys->Compiler); } }; - template <> struct MappingTraits { struct NormalizedOptionInfo { NormalizedOptionInfo(const IO &) : Value(""), Specified(true) {} NormalizedOptionInfo(const IO &, clang::tooling::OptionInfo &OptInfo) : Value(OptInfo.Value), ValueVec(OptInfo.ValueVec), Specified(OptInfo.Specified) {} - clang::tooling::OptionInfo denormalize(const IO &) { clang::tooling::OptionInfo OptInfo; OptInfo.Value = Value; @@ -239,12 +211,10 @@ template <> struct MappingTraits { OptInfo.Specified = Specified; return OptInfo; } - std::string Value; std::vector ValueVec; bool Specified; }; - static void mapping(IO &Io, clang::tooling::OptionInfo &OptInfo) { MappingNormalization Keys( Io, OptInfo); diff --git a/clang/lib/DPCT/AnalysisInfo.cpp b/clang/lib/DPCT/AnalysisInfo.cpp index 25cc18730ae4..fc519e5c9ee6 100644 --- a/clang/lib/DPCT/AnalysisInfo.cpp +++ b/clang/lib/DPCT/AnalysisInfo.cpp @@ -2470,7 +2470,7 @@ std::map DpctGlobalInfo::FunctionCallInMacroMigrateRecord; std::map DpctGlobalInfo::EndOfEmptyMacros; std::map DpctGlobalInfo::BeginOfEmptyMacros; -std::unordered_map> +std::unordered_map> DpctGlobalInfo::FileRelpsMap; std::unordered_map DpctGlobalInfo::MsfInfoMap; diff --git a/clang/lib/DPCT/AnalysisInfo.h b/clang/lib/DPCT/AnalysisInfo.h index 7adbd7ce1e04..e5facd96aa4e 100644 --- a/clang/lib/DPCT/AnalysisInfo.h +++ b/clang/lib/DPCT/AnalysisInfo.h @@ -508,7 +508,7 @@ class DpctFileInfo { getConstantMacroTMSet() { return ConstantMacroTMSet; } - std::vector &getReplacements() { + std::vector &getReplacements() { return PreviousTUReplFromYAML->Replacements; } std::unordered_map> & @@ -1259,7 +1259,7 @@ class DpctGlobalInfo { return FileSetInCompilationDB; } static std::unordered_map> & + std::vector> & getFileRelpsMap() { return FileRelpsMap; } @@ -1659,7 +1659,7 @@ class DpctGlobalInfo { // value: The end location of the macro expansion static std::map BeginOfEmptyMacros; static std::unordered_map> + std::vector> FileRelpsMap; static std::unordered_map MsfInfoMap; diff --git a/clang/lib/DPCT/FileGenerator/GenFiles.cpp b/clang/lib/DPCT/FileGenerator/GenFiles.cpp index 21eb962f15c6..34da311c00c8 100644 --- a/clang/lib/DPCT/FileGenerator/GenFiles.cpp +++ b/clang/lib/DPCT/FileGenerator/GenFiles.cpp @@ -441,7 +441,7 @@ void processAllFiles(StringRef InRoot, StringRef OutRoot, } static void getMainSrcFilesRepls( - std::vector &MainSrcFilesRepls) { + std::vector &MainSrcFilesRepls) { auto &FileRelpsMap = DpctGlobalInfo::getFileRelpsMap(); for (const auto &Entry : FileRelpsMap) for (const auto &Repl : Entry.second) @@ -455,7 +455,7 @@ static void getMainSrcFilesInfo( } static void saveUpdatedMigrationDataIntoYAML( - std::vector &MainSrcFilesRepls, + std::vector &MainSrcFilesRepls, std::vector &MainSrcFilesInfo, clang::tooling::UnifiedPath YamlFile, clang::tooling::UnifiedPath SrcFile, std::unordered_map &MainSrcFileMap) { @@ -511,7 +511,7 @@ int writeReplacementsToFiles( clang::tooling::UnifiedPath &InRoot, std::vector &MainSrcFilesInfo, std::unordered_map &MainSrcFileMap, - std::vector &MainSrcFilesRepls, + std::vector &MainSrcFilesRepls, std::unordered_map> &FileRangesMap, std::unordered_map MainSrcFilesRepls; + std::vector MainSrcFilesRepls; std::vector MainSrcFilesInfo; if (ReplSYCL.empty()) { diff --git a/clang/lib/DPCT/IncMigration/ExternalReplacement.cpp b/clang/lib/DPCT/IncMigration/ExternalReplacement.cpp index fe630a5a8f81..f44f0ef5c946 100644 --- a/clang/lib/DPCT/IncMigration/ExternalReplacement.cpp +++ b/clang/lib/DPCT/IncMigration/ExternalReplacement.cpp @@ -40,7 +40,7 @@ namespace dpct { int save2Yaml( clang::tooling::UnifiedPath &YamlFile, clang::tooling::UnifiedPath &SrcFileName, - const std::vector &Replaces, + const std::vector &Replaces, const std::vector &MainSrcFilesDigest, const std::map> @@ -130,7 +130,7 @@ void loadGDCFromYaml(const clang::tooling::UnifiedPath &Input, void mergeAndUniqueReps( Replacements &Replaces, - const std::vector &PreRepls) { + const std::vector &PreRepls) { bool DupFlag = false; for (const auto &OldR : PreRepls) { @@ -179,8 +179,8 @@ int mergeExternalReps(clang::tooling::UnifiedPath InRootSrcFilePath, llvm::errs() << "Saved new version of " << YamlFile << " file\n"; - std::vector Repls(Replaces.begin(), - Replaces.end()); + std::vector Repls(Replaces.begin(), + Replaces.end()); // For header file, its hash content digest and HasCUDASytax field is not // registed. diff --git a/clang/lib/DPCT/IncMigration/ExternalReplacement.h b/clang/lib/DPCT/IncMigration/ExternalReplacement.h index e79c79e882f1..95c405c79d82 100644 --- a/clang/lib/DPCT/IncMigration/ExternalReplacement.h +++ b/clang/lib/DPCT/IncMigration/ExternalReplacement.h @@ -40,7 +40,7 @@ void loadGDCFromYaml(const clang::tooling::UnifiedPath &Input, int save2Yaml( clang::tooling::UnifiedPath &YamlFile, clang::tooling::UnifiedPath &SrcFileName, - const std::vector &Replaces, + const std::vector &Replaces, const std::vector &MainSrcFilesDigest, const std::map> @@ -48,7 +48,7 @@ int save2Yaml( void mergeAndUniqueReps( clang::tooling::Replacements &Replaces, - const std::vector &PreRepls); + const std::vector &PreRepls); void tryLoadingUpstreamChangesAndUserChanges(); } // namespace dpct diff --git a/clang/lib/DPCT/RulesLang/RulesLang.h b/clang/lib/DPCT/RulesLang/RulesLang.h index 3f8bbb98ef02..bfd624f88520 100644 --- a/clang/lib/DPCT/RulesLang/RulesLang.h +++ b/clang/lib/DPCT/RulesLang/RulesLang.h @@ -508,9 +508,9 @@ class ConstantMemVarMigrationRule : public NamedMigrationRule Info); bool currentIsHost(const VarDecl *VD, std::string VarName); }; diff --git a/clang/lib/DPCT/RulesLang/RulesLangNoneAPIAndType.cpp b/clang/lib/DPCT/RulesLang/RulesLangNoneAPIAndType.cpp index 187471d23d99..01cb30192c66 100644 --- a/clang/lib/DPCT/RulesLang/RulesLangNoneAPIAndType.cpp +++ b/clang/lib/DPCT/RulesLang/RulesLangNoneAPIAndType.cpp @@ -323,8 +323,8 @@ void ConstantMemVarMigrationRule::runRule( } } -void ConstantMemVarMigrationRule::previousHCurrentD( - const VarDecl *VD, tooling::DpctReplacement &R) { +void ConstantMemVarMigrationRule::previousHCurrentD(const VarDecl *VD, + tooling::Replacement &R) { // 1. emit DPCT1055 warning // 2. add a new variable for host // 3. insert dpct::constant_memory and add the info from that replacement @@ -373,11 +373,11 @@ void ConstantMemVarMigrationRule::previousHCurrentD( emplaceTransformation(DeviceRepl); } - R = tooling::DpctReplacement(R.getFilePath(), 0, 0, ""); + R = tooling::Replacement(R.getFilePath(), 0, 0, ""); } -void ConstantMemVarMigrationRule::previousDCurrentH( - const VarDecl *VD, tooling::DpctReplacement &R) { +void ConstantMemVarMigrationRule::previousDCurrentH(const VarDecl *VD, + tooling::Replacement &R) { // 1. change DeviceConstant to HostDeviceConstant // 2. emit DPCT1055 warning (warning info is from previous device case) // 3. add a new variable for host (decl info is from previous device case) @@ -406,8 +406,7 @@ void ConstantMemVarMigrationRule::previousDCurrentH( emplaceTransformation(new InsertText(SL, std::move(NewDecl))); } -void ConstantMemVarMigrationRule::removeHostConstantWarning( - DpctReplacement &R) { +void ConstantMemVarMigrationRule::removeHostConstantWarning(Replacement &R) { std::string ReplStr = R.getReplacementText().str(); // warning text of Diagnostics::HOST_CONSTANT diff --git a/clang/lib/DPCT/TextModification.h b/clang/lib/DPCT/TextModification.h index eef0064c4bc7..83d0350f9747 100644 --- a/clang/lib/DPCT/TextModification.h +++ b/clang/lib/DPCT/TextModification.h @@ -38,10 +38,10 @@ enum ReplacementType { RT_ForSYCLMigration = 0, RT_CUDAWithCodePin }; /// AST Rule. Further Analysis Pass like Merge Pass can happen based /// on this meta info of Replacement. /// eg. Replacement happen at same position may be merged to avoid conflict. -class ExtReplacement : public tooling::DpctReplacement { +class ExtReplacement : public tooling::Replacement { public: /// Creates an invalid (not applicable) replacement. - ExtReplacement() : DpctReplacement() {}; + ExtReplacement() : Replacement() {}; /// Creates a replacement of the range [Offset, Offset+Length) in /// FilePath with ReplacementText. @@ -52,8 +52,8 @@ class ExtReplacement : public tooling::DpctReplacement { ExtReplacement(clang::tooling::UnifiedPath FilePath, unsigned Offset, unsigned Length, StringRef ReplacementText, const TextModification *_TM) - : DpctReplacement(FilePath.getCanonicalPath(), Offset, Length, - ReplacementText), + : Replacement(FilePath.getCanonicalPath(), Offset, Length, + ReplacementText), TM(_TM) {} /// Creates a Replacement of the range [Start, Start+Length) with @@ -61,20 +61,20 @@ class ExtReplacement : public tooling::DpctReplacement { ExtReplacement(const SourceManager &Sources, SourceLocation Start, unsigned Length, StringRef ReplacementText, const TextModification *_TM) - : DpctReplacement(Sources, Start, Length, ReplacementText), TM(_TM) {} + : Replacement(Sources, Start, Length, ReplacementText), TM(_TM) {} /// Creates a Replacement of the given range with ReplacementText. ExtReplacement(const SourceManager &Sources, const CharSourceRange &Range, StringRef ReplacementText, const TextModification *_TM, const LangOptions &LangOpts = LangOptions()) - : DpctReplacement(Sources, Range, ReplacementText, LangOpts), TM(_TM) {} + : Replacement(Sources, Range, ReplacementText, LangOpts), TM(_TM) {} /// Creates a Replacement of the node with ReplacementText. template ExtReplacement(const SourceManager &Sources, const Node &NodeToReplace, StringRef ReplacementText, const TextModification *_TM, const LangOptions &LangOpts = LangOptions()) - : DpctReplacement(Sources, NodeToReplace, ReplacementText, LangOpts), + : Replacement(Sources, NodeToReplace, ReplacementText, LangOpts), TM(_TM) {} void setInsertPosition(InsertPosition IP) { InsertPos = IP; } unsigned int getInsertPosition() const { return InsertPos; } diff --git a/clang/lib/Tooling/Core/Replacement.cpp b/clang/lib/Tooling/Core/Replacement.cpp index 8e927de715d3..27f3fb0e4678 100644 --- a/clang/lib/Tooling/Core/Replacement.cpp +++ b/clang/lib/Tooling/Core/Replacement.cpp @@ -181,9 +181,6 @@ void Replacement::setFromSourceRange(const SourceManager &Sources, ReplacementText); } -#ifdef SYCLomatic_CUSTOMIZATION -#define Replacement DpctReplacement -#endif // SYCLomatic_CUSTOMIZATION Replacement Replacements::getReplacementInChangedCode(const Replacement &R) const { unsigned NewStart = getShiftedCodePosition(R.getOffset()); @@ -732,8 +729,6 @@ AddFileHunk::AddFileHunk(std::string NewFilePath) DeleteFileHunk::DeleteFileHunk(std::string OldFilePath) : Hunk(DeleteFile), OldFilePath(UnifiedPath(OldFilePath).getCanonicalPath()) {} - -#undef Replacement #endif // SYCLomatic_CUSTOMIZATION } // namespace tooling From 989a1a3ce95a5b147243c238a46877f1ce87cf06 Mon Sep 17 00:00:00 2001 From: "Jiang, Zhiwei" Date: Wed, 4 Jun 2025 09:10:01 +0800 Subject: [PATCH 22/53] Update Signed-off-by: Jiang, Zhiwei --- clang/include/clang/Tooling/ReplacementsYaml.h | 16 ++++++++++++++++ clang/lib/DPCT/AnalysisInfo.h | 1 - clang/lib/DPCT/FileGenerator/GenFiles.cpp | 1 + .../lib/DPCT/IncMigration/ExternalReplacement.h | 2 -- clang/lib/DPCT/TextModification.h | 11 ++++------- 5 files changed, 21 insertions(+), 10 deletions(-) diff --git a/clang/include/clang/Tooling/ReplacementsYaml.h b/clang/include/clang/Tooling/ReplacementsYaml.h index c8fcf5cf0725..8da248de707e 100644 --- a/clang/include/clang/Tooling/ReplacementsYaml.h +++ b/clang/include/clang/Tooling/ReplacementsYaml.h @@ -14,9 +14,11 @@ #ifndef LLVM_CLANG_TOOLING_REPLACEMENTSYAML_H #define LLVM_CLANG_TOOLING_REPLACEMENTSYAML_H + #include "clang/Tooling/Refactoring.h" #include "llvm/Support/YAMLTraits.h" #include + LLVM_YAML_IS_SEQUENCE_VECTOR(clang::tooling::Replacement) #ifdef SYCLomatic_CUSTOMIZATION // Keep here only for backward compatibility - begin @@ -52,6 +54,7 @@ ScalarEnumerationTraits::enumeration( namespace llvm { namespace yaml { + /// Specialized MappingTraits to describe how a Replacement is /// (de)serialized. template <> struct MappingTraits { @@ -150,18 +153,22 @@ template <> struct MappingTraits { NormalizedMainSourceFilesDigest(const IO &) : MainSourceFile(""), Digest(""), HasCUDASyntax(false) {} + NormalizedMainSourceFilesDigest(const IO &, clang::tooling::MainSourceFileInfo &R) : MainSourceFile(R.MainSourceFile), Digest(R.Digest), HasCUDASyntax(R.HasCUDASyntax) {} + clang::tooling::MainSourceFileInfo denormalize(const IO &) { return clang::tooling::MainSourceFileInfo(MainSourceFile, Digest, HasCUDASyntax); } + std::string MainSourceFile = ""; std::string Digest = ""; bool HasCUDASyntax = false; }; + static void mapping(IO &Io, clang::tooling::MainSourceFileInfo &R) { MappingNormalization @@ -171,14 +178,18 @@ template <> struct MappingTraits { Io.mapOptional("HasCUDASyntax", Keys->HasCUDASyntax); } }; + template <> struct MappingTraits { struct NormalizedCompileCmds { + NormalizedCompileCmds(const IO &) : MigratedFileName(""), CompileOptions(""), Compiler(""){} + NormalizedCompileCmds(const IO &, clang::tooling::CompilationInfo &CmpInfo) : MigratedFileName(CmpInfo.MigratedFileName), CompileOptions(CmpInfo.CompileOptions), Compiler(CmpInfo.Compiler) {} + clang::tooling::CompilationInfo denormalize(const IO &) { clang::tooling::CompilationInfo CmpInfo; CmpInfo.MigratedFileName = MigratedFileName; @@ -186,10 +197,12 @@ template <> struct MappingTraits { CmpInfo.Compiler = Compiler; return CmpInfo; } + std::string MigratedFileName; std::string CompileOptions; std::string Compiler; }; + static void mapping(IO &Io, clang::tooling::CompilationInfo &CmpInfo) { MappingNormalization Keys(Io, CmpInfo); @@ -198,12 +211,14 @@ template <> struct MappingTraits { Io.mapOptional("Compiler", Keys->Compiler); } }; + template <> struct MappingTraits { struct NormalizedOptionInfo { NormalizedOptionInfo(const IO &) : Value(""), Specified(true) {} NormalizedOptionInfo(const IO &, clang::tooling::OptionInfo &OptInfo) : Value(OptInfo.Value), ValueVec(OptInfo.ValueVec), Specified(OptInfo.Specified) {} + clang::tooling::OptionInfo denormalize(const IO &) { clang::tooling::OptionInfo OptInfo; OptInfo.Value = Value; @@ -215,6 +230,7 @@ template <> struct MappingTraits { std::vector ValueVec; bool Specified; }; + static void mapping(IO &Io, clang::tooling::OptionInfo &OptInfo) { MappingNormalization Keys( Io, OptInfo); diff --git a/clang/lib/DPCT/AnalysisInfo.h b/clang/lib/DPCT/AnalysisInfo.h index e5facd96aa4e..067a681798d0 100644 --- a/clang/lib/DPCT/AnalysisInfo.h +++ b/clang/lib/DPCT/AnalysisInfo.h @@ -37,7 +37,6 @@ #include "clang/Format/Format.h" #include "clang/Frontend/CompilerInstance.h" -#include "clang/Tooling/Core/Replacement.h" #include "clang/Tooling/Core/UnifiedPath.h" llvm::StringRef getReplacedName(const clang::NamedDecl *D); diff --git a/clang/lib/DPCT/FileGenerator/GenFiles.cpp b/clang/lib/DPCT/FileGenerator/GenFiles.cpp index 34da311c00c8..327644e4c299 100644 --- a/clang/lib/DPCT/FileGenerator/GenFiles.cpp +++ b/clang/lib/DPCT/FileGenerator/GenFiles.cpp @@ -440,6 +440,7 @@ void processAllFiles(StringRef InRoot, StringRef OutRoot, } } + static void getMainSrcFilesRepls( std::vector &MainSrcFilesRepls) { auto &FileRelpsMap = DpctGlobalInfo::getFileRelpsMap(); diff --git a/clang/lib/DPCT/IncMigration/ExternalReplacement.h b/clang/lib/DPCT/IncMigration/ExternalReplacement.h index 95c405c79d82..4e788b927e84 100644 --- a/clang/lib/DPCT/IncMigration/ExternalReplacement.h +++ b/clang/lib/DPCT/IncMigration/ExternalReplacement.h @@ -10,9 +10,7 @@ #define __EXTERNAL_REPLACEMENT_H__ #include "clang/Tooling/Core/Replacement.h" -#include "clang/Tooling/Core/UnifiedPath.h" #include "llvm/ADT/StringRef.h" - #include #include diff --git a/clang/lib/DPCT/TextModification.h b/clang/lib/DPCT/TextModification.h index 83d0350f9747..7636db4fbddd 100644 --- a/clang/lib/DPCT/TextModification.h +++ b/clang/lib/DPCT/TextModification.h @@ -41,7 +41,7 @@ enum ReplacementType { RT_ForSYCLMigration = 0, RT_CUDAWithCodePin }; class ExtReplacement : public tooling::Replacement { public: /// Creates an invalid (not applicable) replacement. - ExtReplacement() : Replacement() {}; + ExtReplacement() : Replacement(){}; /// Creates a replacement of the range [Offset, Offset+Length) in /// FilePath with ReplacementText. @@ -49,12 +49,9 @@ class ExtReplacement : public tooling::Replacement { /// \param FilePath A source file accessible via a SourceManager. /// \param Offset The byte offset of the start of the range in the file. /// \param Length The length of the range in bytes. - ExtReplacement(clang::tooling::UnifiedPath FilePath, unsigned Offset, - unsigned Length, StringRef ReplacementText, - const TextModification *_TM) - : Replacement(FilePath.getCanonicalPath(), Offset, Length, - ReplacementText), - TM(_TM) {} + ExtReplacement(clang::tooling::UnifiedPath FilePath, unsigned Offset, unsigned Length, + StringRef ReplacementText, const TextModification *_TM) + : Replacement(FilePath.getCanonicalPath(), Offset, Length, ReplacementText), TM(_TM) {} /// Creates a Replacement of the range [Start, Start+Length) with /// ReplacementText. From ba244bc0cb80d209583367e68254e417d96f58b7 Mon Sep 17 00:00:00 2001 From: "Jiang, Zhiwei" Date: Wed, 4 Jun 2025 09:11:44 +0800 Subject: [PATCH 23/53] 1 Signed-off-by: Jiang, Zhiwei --- clang/include/clang/Tooling/ReplacementsYaml.h | 1 + 1 file changed, 1 insertion(+) diff --git a/clang/include/clang/Tooling/ReplacementsYaml.h b/clang/include/clang/Tooling/ReplacementsYaml.h index 8da248de707e..a31dd16526cb 100644 --- a/clang/include/clang/Tooling/ReplacementsYaml.h +++ b/clang/include/clang/Tooling/ReplacementsYaml.h @@ -226,6 +226,7 @@ template <> struct MappingTraits { OptInfo.Specified = Specified; return OptInfo; } + std::string Value; std::vector ValueVec; bool Specified; From 4e2dca39355d698db2988bf402315e6728db46fc Mon Sep 17 00:00:00 2001 From: "Jiang, Zhiwei" Date: Wed, 4 Jun 2025 09:47:52 +0800 Subject: [PATCH 24/53] Update Signed-off-by: Jiang, Zhiwei --- .../include/clang/Tooling/Core/Replacement.h | 69 ------ .../include/clang/Tooling/ReplacementsYaml.h | 131 ----------- clang/lib/DPCT/AnalysisInfo.cpp | 2 - clang/lib/DPCT/AnalysisInfo.h | 8 - .../DPCT/IncMigration/ExternalReplacement.cpp | 4 +- .../DPCT/IncMigration/ExternalReplacement.h | 6 +- clang/lib/DPCT/IncMigration/ReMigration.cpp | 20 +- clang/lib/DPCT/IncMigration/ReMigration.h | 217 ++++++++++++++++++ clang/lib/Tooling/Core/Replacement.cpp | 7 - 9 files changed, 237 insertions(+), 227 deletions(-) create mode 100644 clang/lib/DPCT/IncMigration/ReMigration.h diff --git a/clang/include/clang/Tooling/Core/Replacement.h b/clang/include/clang/Tooling/Core/Replacement.h index d9ce05c59c62..40fb47fb378d 100644 --- a/clang/include/clang/Tooling/Core/Replacement.h +++ b/clang/include/clang/Tooling/Core/Replacement.h @@ -428,75 +428,6 @@ struct OptionInfo { std::vector ValueVec; bool Specified = true; }; - -class Hunk { -public: - enum HunkType : unsigned { - ModifyFile = 0, - AddFile, - DeleteFile, - MoveFile, - Unspecified - }; - -private: - HunkType HT = Unspecified; - -public: - Hunk(HunkType HT) : HT(HT) {} - HunkType getHunkType() const { return HT; } - virtual ~Hunk() = default; -}; - -class ModifyFileHunk : public Hunk, public Replacement { -public: - ModifyFileHunk() : Hunk(ModifyFile), Replacement() {} - ModifyFileHunk(const std::string &FilePath, unsigned Offset, unsigned Length, - const std::string &ReplacementText) - : Hunk(ModifyFile), - Replacement(FilePath, Offset, Length, ReplacementText) {} - ModifyFileHunk(const Replacement &R) : Hunk(ModifyFile), Replacement(R) {} -}; - -class AddFileHunk : public Hunk { - std::string NewFilePath; - -public: - AddFileHunk() : Hunk(AddFile) {} - AddFileHunk(std::string NewFilePath); - const std::string &getNewFilePath() const { return NewFilePath; } -}; - -class DeleteFileHunk : public Hunk { - std::string OldFilePath; - -public: - DeleteFileHunk() : Hunk(DeleteFile) {} - DeleteFileHunk(std::string OldFilePath); - const std::string &getOldFilePath() const { return OldFilePath; } -}; - -class MoveFileHunk : public Hunk, public Replacement { - std::string NewFilePath; - -public: - MoveFileHunk() : Hunk(MoveFile), Replacement() {} - MoveFileHunk(const std::string &FilePath, unsigned Offset, unsigned Length, - const std::string &ReplacementText, - const std::string &NewFilePath) - : Hunk(MoveFile), Replacement(FilePath, Offset, Length, ReplacementText), - NewFilePath(std::move(NewFilePath)) {} - MoveFileHunk(const Replacement &R, const std::string &NewFilePath) - : Hunk(MoveFile), Replacement(R), NewFilePath(NewFilePath) {} - std::string getNewFilePath() const { return NewFilePath; } -}; - -struct GitDiffChanges { - std::vector ModifyFileHunks; - std::vector AddFileHunks; - std::vector DeleteFileHunks; - std::vector MoveFileHunks; -}; #endif // SYCLomatic_CUSTOMIZATION /// Collection of Replacements generated from a single translation unit. struct TranslationUnitReplacements { diff --git a/clang/include/clang/Tooling/ReplacementsYaml.h b/clang/include/clang/Tooling/ReplacementsYaml.h index a31dd16526cb..2e5e9703f8e1 100644 --- a/clang/include/clang/Tooling/ReplacementsYaml.h +++ b/clang/include/clang/Tooling/ReplacementsYaml.h @@ -31,25 +31,6 @@ LLVM_YAML_IS_STRING_MAP(std::vector) LLVM_YAML_IS_SEQUENCE_VECTOR(clang::tooling::MainSourceFileInfo) LLVM_YAML_IS_STRING_MAP(std::vector) LLVM_YAML_IS_STRING_MAP(clang::tooling::OptionInfo) -LLVM_YAML_IS_SEQUENCE_VECTOR(clang::tooling::ModifyFileHunk) -LLVM_YAML_IS_SEQUENCE_VECTOR(clang::tooling::AddFileHunk) -LLVM_YAML_IS_SEQUENCE_VECTOR(clang::tooling::DeleteFileHunk) -LLVM_YAML_IS_SEQUENCE_VECTOR(clang::tooling::MoveFileHunk) -LLVM_YAML_DECLARE_ENUM_TRAITS(clang::tooling::Hunk::HunkType) -namespace llvm { -namespace yaml { -inline void -ScalarEnumerationTraits::enumeration( - IO &io, clang::tooling::Hunk::HunkType &value) { - io.enumCase(value, "ModifyFile", clang::tooling::Hunk::HunkType::ModifyFile); - io.enumCase(value, "AddFile", clang::tooling::Hunk::HunkType::AddFile); - io.enumCase(value, "DeleteFile", clang::tooling::Hunk::HunkType::DeleteFile); - io.enumCase(value, "MoveFile", clang::tooling::Hunk::HunkType::MoveFile); - io.enumCase(value, "Unspecified", - clang::tooling::Hunk::HunkType::Unspecified); -} -} // namespace yaml -} // namespace llvm #endif // SYCLomatic_CUSTOMIZATION namespace llvm { @@ -241,118 +222,6 @@ template <> struct MappingTraits { } }; -template <> struct MappingTraits { - struct NormalizedModifyFileHunk { - NormalizedModifyFileHunk(const IO &io) : Offset(0), Length(0) {} - NormalizedModifyFileHunk(const IO &io, clang::tooling::ModifyFileHunk &H) - : FilePath(H.getFilePath()), Offset(H.getOffset()), - Length(H.getLength()), ReplacementText(H.getReplacementText()) {} - - clang::tooling::ModifyFileHunk denormalize(const IO &) { - clang::tooling::ModifyFileHunk H(FilePath, Offset, Length, - ReplacementText); - return H; - } - - std::string FilePath; - unsigned int Offset; - unsigned int Length; - std::string ReplacementText; - }; - - static void mapping(IO &Io, clang::tooling::ModifyFileHunk &H) { - MappingNormalization - Keys(Io, H); - Io.mapRequired("FilePath", Keys->FilePath); - Io.mapRequired("Offset", Keys->Offset); - Io.mapRequired("Length", Keys->Length); - Io.mapRequired("ReplacementText", Keys->ReplacementText); - } -}; - -template <> struct MappingTraits { - struct NormalizedAddFileHunk { - NormalizedAddFileHunk(const IO &io) {} - NormalizedAddFileHunk(const IO &io, clang::tooling::AddFileHunk &H) - : NewFilePath(H.getNewFilePath()) {} - - clang::tooling::AddFileHunk denormalize(const IO &io) { - clang::tooling::AddFileHunk H(NewFilePath); - return H; - } - - std::string NewFilePath; - }; - static void mapping(IO &Io, clang::tooling::AddFileHunk &H) { - MappingNormalization - Keys(Io, H); - Io.mapRequired("NewFilePath", Keys->NewFilePath); - } -}; - -template <> struct MappingTraits { - struct NormalizedDeleteFileHunk { - NormalizedDeleteFileHunk(const IO &io) {} - NormalizedDeleteFileHunk(const IO &io, clang::tooling::DeleteFileHunk &H) - : OldFilePath(H.getOldFilePath()) {} - - clang::tooling::DeleteFileHunk denormalize(const IO &io) { - clang::tooling::DeleteFileHunk H(OldFilePath); - return H; - } - - std::string OldFilePath; - }; - static void mapping(IO &Io, clang::tooling::DeleteFileHunk &H) { - MappingNormalization - Keys(Io, H); - Io.mapRequired("OldFilePath", Keys->OldFilePath); - } -}; - -template <> struct MappingTraits { - struct NormalizedMoveFileHunk { - NormalizedMoveFileHunk(const IO &io) : Offset(0), Length(0) {} - NormalizedMoveFileHunk(const IO &io, clang::tooling::MoveFileHunk &H) - : FilePath(H.getFilePath()), Offset(H.getOffset()), - Length(H.getLength()), ReplacementText(H.getReplacementText()), - NewFilePath(H.getNewFilePath()) {} - - clang::tooling::MoveFileHunk denormalize(const IO &io) { - clang::tooling::MoveFileHunk H(FilePath, Offset, Length, ReplacementText, - NewFilePath); - return H; - } - - std::string FilePath; - unsigned int Offset; - unsigned int Length; - std::string ReplacementText; - std::string NewFilePath; - }; - - static void mapping(IO &Io, clang::tooling::MoveFileHunk &H) { - MappingNormalization - Keys(Io, H); - Io.mapRequired("FilePath", Keys->FilePath); - Io.mapRequired("Offset", Keys->Offset); - Io.mapRequired("Length", Keys->Length); - Io.mapRequired("ReplacementText", Keys->ReplacementText); - Io.mapRequired("NewFilePath", Keys->NewFilePath); - } -}; - -template <> struct MappingTraits { - static void mapping(IO &Io, clang::tooling::GitDiffChanges &GDC) { - Io.mapOptional("ModifyFileHunks", GDC.ModifyFileHunks); - Io.mapOptional("AddFileHunks", GDC.AddFileHunks); - Io.mapOptional("DeleteFileHunks", GDC.DeleteFileHunks); - Io.mapOptional("MoveFileHunks", GDC.MoveFileHunks); - } -}; - // Keep here only for backward compatibility - begin template <> struct MappingTraits { struct NormalizedHelperFuncForYaml { diff --git a/clang/lib/DPCT/AnalysisInfo.cpp b/clang/lib/DPCT/AnalysisInfo.cpp index fc519e5c9ee6..722d79dc2ae7 100644 --- a/clang/lib/DPCT/AnalysisInfo.cpp +++ b/clang/lib/DPCT/AnalysisInfo.cpp @@ -2557,8 +2557,6 @@ bool DpctGlobalInfo::IsAfterBitsStdcxx = false; std::map> DpctGlobalInfo::AfterBitsStdcxxFiles; -clang::tooling::GitDiffChanges DpctGlobalInfo::UpstreamChanges; -clang::tooling::GitDiffChanges DpctGlobalInfo::UserChanges; ///// class DpctNameGenerator ///// void DpctNameGenerator::printName(const FunctionDecl *FD, diff --git a/clang/lib/DPCT/AnalysisInfo.h b/clang/lib/DPCT/AnalysisInfo.h index 067a681798d0..06958a3785ec 100644 --- a/clang/lib/DPCT/AnalysisInfo.h +++ b/clang/lib/DPCT/AnalysisInfo.h @@ -1404,12 +1404,6 @@ class DpctGlobalInfo { getAfterBitsStdcxxFilesMap() { return AfterBitsStdcxxFiles; } - static clang::tooling::GitDiffChanges &getUpstreamChanges() { - return UpstreamChanges; - } - static clang::tooling::GitDiffChanges &getUserChanges() { - return UserChanges; - } std::shared_ptr insertFile(const clang::tooling::UnifiedPath &FilePath) { return insertObject(FileMap, FilePath); @@ -1752,8 +1746,6 @@ class DpctGlobalInfo { static std::map> AfterBitsStdcxxFiles; - static clang::tooling::GitDiffChanges UpstreamChanges; - static clang::tooling::GitDiffChanges UserChanges; }; /// Generate mangle name of FunctionDecl as key of DeviceFunctionInfo. diff --git a/clang/lib/DPCT/IncMigration/ExternalReplacement.cpp b/clang/lib/DPCT/IncMigration/ExternalReplacement.cpp index f44f0ef5c946..93ee7f8bbb72 100644 --- a/clang/lib/DPCT/IncMigration/ExternalReplacement.cpp +++ b/clang/lib/DPCT/IncMigration/ExternalReplacement.cpp @@ -119,12 +119,12 @@ int loadTUFromYaml(const clang::tooling::UnifiedPath &Input, } void loadGDCFromYaml(const clang::tooling::UnifiedPath &Input, - clang::tooling::GitDiffChanges &GDC) { + clang::dpct::GitDiffChanges &GDC) { int status = loadFromYaml(Input, GDC); if (status) { llvm::errs() << "Failed to load git diff Changes from " << Input.getCanonicalPath() << "\n"; - GDC = clang::tooling::GitDiffChanges(); + GDC = clang::dpct::GitDiffChanges(); } } diff --git a/clang/lib/DPCT/IncMigration/ExternalReplacement.h b/clang/lib/DPCT/IncMigration/ExternalReplacement.h index 4e788b927e84..99c9376394b1 100644 --- a/clang/lib/DPCT/IncMigration/ExternalReplacement.h +++ b/clang/lib/DPCT/IncMigration/ExternalReplacement.h @@ -9,8 +9,8 @@ #ifndef __EXTERNAL_REPLACEMENT_H__ #define __EXTERNAL_REPLACEMENT_H__ -#include "clang/Tooling/Core/Replacement.h" -#include "llvm/ADT/StringRef.h" +#include "IncMigration/ReMigration.h" + #include #include @@ -34,7 +34,7 @@ int mergeExternalReps(clang::tooling::UnifiedPath InRootSrcFilePath, int loadTUFromYaml(const clang::tooling::UnifiedPath &Input, clang::tooling::TranslationUnitReplacements &TU); void loadGDCFromYaml(const clang::tooling::UnifiedPath &Input, - clang::tooling::GitDiffChanges &GDC); + clang::dpct::GitDiffChanges &GDC); int save2Yaml( clang::tooling::UnifiedPath &YamlFile, clang::tooling::UnifiedPath &SrcFileName, diff --git a/clang/lib/DPCT/IncMigration/ReMigration.cpp b/clang/lib/DPCT/IncMigration/ReMigration.cpp index 192615b0d22f..c4c7415880f1 100644 --- a/clang/lib/DPCT/IncMigration/ReMigration.cpp +++ b/clang/lib/DPCT/IncMigration/ReMigration.cpp @@ -6,12 +6,23 @@ // //===----------------------------------------------------------------------===// +#include "ReMigration.h" + #include "AnalysisInfo.h" #include "ExternalReplacement.h" namespace clang::dpct { - -static void dumpGitDiffChanges(const clang::tooling::GitDiffChanges &GHC) { +static GitDiffChanges UpstreamChanges; +static GitDiffChanges UserChanges; +AddFileHunk::AddFileHunk(std::string NewFilePath) + : Hunk(AddFile), + NewFilePath(tooling::UnifiedPath(NewFilePath).getCanonicalPath()) {} +DeleteFileHunk::DeleteFileHunk(std::string OldFilePath) + : Hunk(DeleteFile), + OldFilePath(tooling::UnifiedPath(OldFilePath).getCanonicalPath()) {} +GitDiffChanges &getUpstreamChanges() { return UpstreamChanges; } +GitDiffChanges &getUserChanges() { return UserChanges; } +static void dumpGitDiffChanges(const GitDiffChanges &GHC) { llvm::errs() << "GitDiffChanges:\n"; llvm::errs() << " ModifyFileHunks:\n"; for (const auto &Hunk : GHC.ModifyFileHunks) { @@ -49,11 +60,10 @@ void tryLoadingUpstreamChangesAndUserChanges() { llvm::sys::path::append(UserChangesFilePath, "UserChanges.yaml"); if (llvm::sys::fs::exists(UpstreamChangesFilePath)) { - loadGDCFromYaml(UpstreamChangesFilePath, - DpctGlobalInfo::getUpstreamChanges()); + ::loadGDCFromYaml(UpstreamChangesFilePath, getUpstreamChanges()); } if (llvm::sys::fs::exists(UserChangesFilePath)) { - loadGDCFromYaml(UserChangesFilePath, DpctGlobalInfo::getUserChanges()); + ::loadGDCFromYaml(UserChangesFilePath, getUserChanges()); } } } // namespace clang::dpct diff --git a/clang/lib/DPCT/IncMigration/ReMigration.h b/clang/lib/DPCT/IncMigration/ReMigration.h new file mode 100644 index 000000000000..cc0154883c73 --- /dev/null +++ b/clang/lib/DPCT/IncMigration/ReMigration.h @@ -0,0 +1,217 @@ +//===----------------------- ReMigration.h --------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef __DPCT_REMIGRATION_H__ +#define __DPCT_REMIGRATION_H__ + +#include "clang/Tooling/Core/Replacement.h" +#include "clang/Tooling/ReplacementsYaml.h" + +namespace clang { +namespace dpct { +class Hunk { +public: + enum HunkType : unsigned { + ModifyFile = 0, + AddFile, + DeleteFile, + MoveFile, + Unspecified + }; + +private: + HunkType HT = Unspecified; + +public: + Hunk(HunkType HT) : HT(HT) {} + HunkType getHunkType() const { return HT; } + virtual ~Hunk() = default; +}; + +class ModifyFileHunk : public Hunk, public tooling::Replacement { +public: + ModifyFileHunk() : Hunk(ModifyFile), Replacement() {} + ModifyFileHunk(const std::string &FilePath, unsigned Offset, unsigned Length, + const std::string &ReplacementText) + : Hunk(ModifyFile), + Replacement(FilePath, Offset, Length, ReplacementText) {} + ModifyFileHunk(const Replacement &R) : Hunk(ModifyFile), Replacement(R) {} +}; + +class AddFileHunk : public Hunk { + std::string NewFilePath; + +public: + AddFileHunk() : Hunk(AddFile) {} + AddFileHunk(std::string NewFilePath); + const std::string &getNewFilePath() const { return NewFilePath; } +}; + +class DeleteFileHunk : public Hunk { + std::string OldFilePath; + +public: + DeleteFileHunk() : Hunk(DeleteFile) {} + DeleteFileHunk(std::string OldFilePath); + const std::string &getOldFilePath() const { return OldFilePath; } +}; + +class MoveFileHunk : public Hunk, public tooling::Replacement { + std::string NewFilePath; + +public: + MoveFileHunk() : Hunk(MoveFile), Replacement() {} + MoveFileHunk(const std::string &FilePath, unsigned Offset, unsigned Length, + const std::string &ReplacementText, + const std::string &NewFilePath) + : Hunk(MoveFile), Replacement(FilePath, Offset, Length, ReplacementText), + NewFilePath(std::move(NewFilePath)) {} + MoveFileHunk(const Replacement &R, const std::string &NewFilePath) + : Hunk(MoveFile), Replacement(R), NewFilePath(NewFilePath) {} + std::string getNewFilePath() const { return NewFilePath; } +}; + +struct GitDiffChanges { + std::vector ModifyFileHunks; + std::vector AddFileHunks; + std::vector DeleteFileHunks; + std::vector MoveFileHunks; +}; +GitDiffChanges &getUpstreamChanges(); +GitDiffChanges &getUserChanges(); +} // namespace dpct +} // namespace clang + +LLVM_YAML_IS_SEQUENCE_VECTOR(clang::dpct::ModifyFileHunk) +LLVM_YAML_IS_SEQUENCE_VECTOR(clang::dpct::AddFileHunk) +LLVM_YAML_IS_SEQUENCE_VECTOR(clang::dpct::DeleteFileHunk) +LLVM_YAML_IS_SEQUENCE_VECTOR(clang::dpct::MoveFileHunk) +LLVM_YAML_DECLARE_ENUM_TRAITS(clang::dpct::Hunk::HunkType) +namespace llvm { +namespace yaml { +inline void ScalarEnumerationTraits::enumeration( + IO &io, clang::dpct::Hunk::HunkType &value) { + io.enumCase(value, "ModifyFile", clang::dpct::Hunk::HunkType::ModifyFile); + io.enumCase(value, "AddFile", clang::dpct::Hunk::HunkType::AddFile); + io.enumCase(value, "DeleteFile", clang::dpct::Hunk::HunkType::DeleteFile); + io.enumCase(value, "MoveFile", clang::dpct::Hunk::HunkType::MoveFile); + io.enumCase(value, "Unspecified", clang::dpct::Hunk::HunkType::Unspecified); +} + +template <> struct MappingTraits { + struct NormalizedModifyFileHunk { + NormalizedModifyFileHunk(const IO &io) : Offset(0), Length(0) {} + NormalizedModifyFileHunk(const IO &io, clang::dpct::ModifyFileHunk &H) + : FilePath(H.getFilePath()), Offset(H.getOffset()), + Length(H.getLength()), ReplacementText(H.getReplacementText()) {} + + clang::dpct::ModifyFileHunk denormalize(const IO &) { + clang::dpct::ModifyFileHunk H(FilePath, Offset, Length, ReplacementText); + return H; + } + + std::string FilePath; + unsigned int Offset; + unsigned int Length; + std::string ReplacementText; + }; + + static void mapping(IO &Io, clang::dpct::ModifyFileHunk &H) { + MappingNormalization + Keys(Io, H); + Io.mapRequired("FilePath", Keys->FilePath); + Io.mapRequired("Offset", Keys->Offset); + Io.mapRequired("Length", Keys->Length); + Io.mapRequired("ReplacementText", Keys->ReplacementText); + } +}; + +template <> struct MappingTraits { + struct NormalizedAddFileHunk { + NormalizedAddFileHunk(const IO &io) {} + NormalizedAddFileHunk(const IO &io, clang::dpct::AddFileHunk &H) + : NewFilePath(H.getNewFilePath()) {} + + clang::dpct::AddFileHunk denormalize(const IO &io) { + clang::dpct::AddFileHunk H(NewFilePath); + return H; + } + + std::string NewFilePath; + }; + static void mapping(IO &Io, clang::dpct::AddFileHunk &H) { + MappingNormalization Keys( + Io, H); + Io.mapRequired("NewFilePath", Keys->NewFilePath); + } +}; + +template <> struct MappingTraits { + struct NormalizedDeleteFileHunk { + NormalizedDeleteFileHunk(const IO &io) {} + NormalizedDeleteFileHunk(const IO &io, clang::dpct::DeleteFileHunk &H) + : OldFilePath(H.getOldFilePath()) {} + + clang::dpct::DeleteFileHunk denormalize(const IO &io) { + clang::dpct::DeleteFileHunk H(OldFilePath); + return H; + } + + std::string OldFilePath; + }; + static void mapping(IO &Io, clang::dpct::DeleteFileHunk &H) { + MappingNormalization + Keys(Io, H); + Io.mapRequired("OldFilePath", Keys->OldFilePath); + } +}; + +template <> struct MappingTraits { + struct NormalizedMoveFileHunk { + NormalizedMoveFileHunk(const IO &io) : Offset(0), Length(0) {} + NormalizedMoveFileHunk(const IO &io, clang::dpct::MoveFileHunk &H) + : FilePath(H.getFilePath()), Offset(H.getOffset()), + Length(H.getLength()), ReplacementText(H.getReplacementText()), + NewFilePath(H.getNewFilePath()) {} + + clang::dpct::MoveFileHunk denormalize(const IO &io) { + clang::dpct::MoveFileHunk H(FilePath, Offset, Length, ReplacementText, + NewFilePath); + return H; + } + + std::string FilePath; + unsigned int Offset; + unsigned int Length; + std::string ReplacementText; + std::string NewFilePath; + }; + + static void mapping(IO &Io, clang::dpct::MoveFileHunk &H) { + MappingNormalization + Keys(Io, H); + Io.mapRequired("FilePath", Keys->FilePath); + Io.mapRequired("Offset", Keys->Offset); + Io.mapRequired("Length", Keys->Length); + Io.mapRequired("ReplacementText", Keys->ReplacementText); + Io.mapRequired("NewFilePath", Keys->NewFilePath); + } +}; + +template <> struct MappingTraits { + static void mapping(IO &Io, clang::dpct::GitDiffChanges &GDC) { + Io.mapOptional("ModifyFileHunks", GDC.ModifyFileHunks); + Io.mapOptional("AddFileHunks", GDC.AddFileHunks); + Io.mapOptional("DeleteFileHunks", GDC.DeleteFileHunks); + Io.mapOptional("MoveFileHunks", GDC.MoveFileHunks); + } +}; +} // namespace yaml +} // namespace llvm + +#endif // __DPCT_REMIGRATION_H__ diff --git a/clang/lib/Tooling/Core/Replacement.cpp b/clang/lib/Tooling/Core/Replacement.cpp index 27f3fb0e4678..76450d6c25db 100644 --- a/clang/lib/Tooling/Core/Replacement.cpp +++ b/clang/lib/Tooling/Core/Replacement.cpp @@ -723,13 +723,6 @@ std::map groupReplacementsByFile( } return Result; } -#ifdef SYCLomatic_CUSTOMIZATION -AddFileHunk::AddFileHunk(std::string NewFilePath) - : Hunk(AddFile), NewFilePath(UnifiedPath(NewFilePath).getCanonicalPath()) {} -DeleteFileHunk::DeleteFileHunk(std::string OldFilePath) - : Hunk(DeleteFile), - OldFilePath(UnifiedPath(OldFilePath).getCanonicalPath()) {} -#endif // SYCLomatic_CUSTOMIZATION } // namespace tooling } // namespace clang From 023a3bb3b1b3108513c26f692c4afcdc3d942b7b Mon Sep 17 00:00:00 2001 From: "Jiang, Zhiwei" Date: Wed, 4 Jun 2025 09:50:03 +0800 Subject: [PATCH 25/53] 1 Signed-off-by: Jiang, Zhiwei --- clang/lib/DPCT/AnalysisInfo.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/clang/lib/DPCT/AnalysisInfo.cpp b/clang/lib/DPCT/AnalysisInfo.cpp index 722d79dc2ae7..36cd3cfe5405 100644 --- a/clang/lib/DPCT/AnalysisInfo.cpp +++ b/clang/lib/DPCT/AnalysisInfo.cpp @@ -2557,7 +2557,6 @@ bool DpctGlobalInfo::IsAfterBitsStdcxx = false; std::map> DpctGlobalInfo::AfterBitsStdcxxFiles; - ///// class DpctNameGenerator ///// void DpctNameGenerator::printName(const FunctionDecl *FD, llvm::raw_ostream &OS) { From 4752c67e78b73848b98d621ec611a97389a56f7f Mon Sep 17 00:00:00 2001 From: "Jiang, Zhiwei" Date: Wed, 4 Jun 2025 09:55:32 +0800 Subject: [PATCH 26/53] WIP Signed-off-by: Jiang, Zhiwei --- clang/lib/DPCT/IncMigration/ReMigration.cpp | 30 +++++++++++---------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/clang/lib/DPCT/IncMigration/ReMigration.cpp b/clang/lib/DPCT/IncMigration/ReMigration.cpp index b532e8712c2d..ee330db70134 100644 --- a/clang/lib/DPCT/IncMigration/ReMigration.cpp +++ b/clang/lib/DPCT/IncMigration/ReMigration.cpp @@ -84,16 +84,16 @@ calculateUpdatedRanges(const clang::tooling::Replacements &Repls, Repls.getShiftedCodePosition(R.getOffset() + R.getLength()); if (BOffset > EOffset) continue; - (void)Result.add(clang::tooling::DpctReplacement( + (void)Result.add(tooling::Replacement( R.getFilePath(), BOffset, EOffset - BOffset, R.getReplacementText())); } return Result; } -std::map> +std::map> groupReplcementsFileByFile( - const std::vector &Repls) { - std::map> Result; + const std::vector &Repls) { + std::map> Result; for (const auto &R : Repls) { Result[R.getFilePath().str()].push_back(R); } @@ -136,19 +136,21 @@ groupReplcementsFileByFile( // Repl_C_x will be ignored during this merge. // 2. Shfit Repl_C_y with Repl_A, called Repl_D. // 3. Merge Repl_D and Repl_B. May have conflicts. + +// !!!WIP!!! std::map -reMigrationMerge(const clang::tooling::GitDiffChanges &Repl_A, - const std::vector &Repl_B, - const std::vector &Repl_C1, - const clang::tooling::GitDiffChanges &Repl_C2) { +reMigrationMerge(const GitDiffChanges &Repl_A, + const std::vector &Repl_B, + const std::vector &Repl_C1, + const GitDiffChanges &Repl_C2) { assert(Repl_C2.AddFileHunks.empty() && Repl_C2.DeleteFileHunks.empty() && Repl_C2.MoveFileHunks.empty() && "Repl_C2 should only have ModifiyFileHunks."); - std::vector Repl_C; + std::vector Repl_C; // Merge Repl_C1 and Repl_C2 Repl_C.insert(Repl_C.end(), Repl_C1.begin(), Repl_C1.end()); for (const auto &Hunk : Repl_C2.ModifyFileHunks) { - clang::tooling::DpctReplacement Replacement( + tooling::Replacement Replacement( Hunk.getFilePath(), Hunk.getOffset(), Hunk.getLength(), Hunk.getReplacementText()); Repl_C.push_back(Replacement); @@ -164,7 +166,7 @@ reMigrationMerge(const clang::tooling::GitDiffChanges &Repl_A, Hunk.getLength(); } (void)ModifiedParts[Hunk.getFilePath().str()].add( - clang::tooling::DpctReplacement(Hunk.getFilePath().str(), + tooling::Replacement(Hunk.getFilePath().str(), Hunk.getOffset(), Hunk.getLength(), Hunk.getReplacementText())); } @@ -173,7 +175,7 @@ reMigrationMerge(const clang::tooling::GitDiffChanges &Repl_A, DeletedParts[Hunk.getFilePath().str()][Hunk.getOffset()] = Hunk.getLength(); } (void)ModifiedParts[Hunk.getFilePath().str()].add( - clang::tooling::DpctReplacement(Hunk.getFilePath().str(), + tooling::Replacement(Hunk.getFilePath().str(), Hunk.getOffset(), Hunk.getLength(), Hunk.getReplacementText())); } @@ -189,7 +191,7 @@ reMigrationMerge(const clang::tooling::GitDiffChanges &Repl_A, const auto &It = DeletedParts.find(Repl.getFilePath().str()); if (It == DeletedParts.end()) { (void)Repl_C_y[Repl.getFilePath().str()].add( - clang::tooling::DpctReplacement(Repl.getFilePath().str(), + tooling::Replacement(Repl.getFilePath().str(), Repl.getOffset(), Repl.getLength(), Repl.getReplacementText())); continue; @@ -204,7 +206,7 @@ reMigrationMerge(const clang::tooling::GitDiffChanges &Repl_A, } } (void)Repl_C_y[Repl.getFilePath().str()].add( - clang::tooling::DpctReplacement(Repl.getFilePath().str(), + tooling::Replacement(Repl.getFilePath().str(), Repl.getOffset(), Repl.getLength(), Repl.getReplacementText())); } From a1fe561cfd6e83e1afc0325bc1a9cb3bb6d3784d Mon Sep 17 00:00:00 2001 From: "Jiang, Zhiwei" Date: Wed, 4 Jun 2025 13:34:40 +0800 Subject: [PATCH 27/53] add merge Signed-off-by: Jiang, Zhiwei --- clang/lib/DPCT/IncMigration/ReMigration.cpp | 201 ++++++++++++++++++-- 1 file changed, 180 insertions(+), 21 deletions(-) diff --git a/clang/lib/DPCT/IncMigration/ReMigration.cpp b/clang/lib/DPCT/IncMigration/ReMigration.cpp index ee330db70134..602957d046bd 100644 --- a/clang/lib/DPCT/IncMigration/ReMigration.cpp +++ b/clang/lib/DPCT/IncMigration/ReMigration.cpp @@ -7,12 +7,18 @@ //===----------------------------------------------------------------------===// #include "ReMigration.h" - #include "AnalysisInfo.h" #include "ExternalReplacement.h" + +#include "clang/AST/Expr.h" #include "clang/Tooling/Core/Replacement.h" +#include "clang/Tooling/Core/UnifiedPath.h" +#include "llvm/ADT/StringRef.h" #include "llvm/Support/raw_ostream.h" +#include +#include + namespace clang::dpct { static GitDiffChanges UpstreamChanges; static GitDiffChanges UserChanges; @@ -91,7 +97,7 @@ calculateUpdatedRanges(const clang::tooling::Replacements &Repls, } std::map> -groupReplcementsFileByFile( +groupReplcementsByFile( const std::vector &Repls) { std::map> Result; for (const auto &R : Repls) { @@ -100,6 +106,157 @@ groupReplcementsFileByFile( return Result; } +StringRef getLineString(clang::tooling::UnifiedPath FilePath, + unsigned LineNumber) { + auto FileInfo = DpctGlobalInfo::getInstance().insertFile(FilePath); + StringRef Line = FileInfo->getLineString(LineNumber); + return Line; +} +unsigned getLineNumber(clang::tooling::UnifiedPath FilePath, unsigned Offset) { + auto FileInfo = DpctGlobalInfo::getInstance().insertFile(FilePath); + return FileInfo->getLineNumber(Offset); +} +unsigned getLineBeginOffset(clang::tooling::UnifiedPath FilePath, + unsigned LineNumber) { + auto FileInfo = DpctGlobalInfo::getInstance().insertFile(FilePath); + return FileInfo->getLineInfo(LineNumber).Offset; +} + +std::map +convertReplcementsLineString(const std::vector &Repls) { + tooling::UnifiedPath FilePath(Repls[0].getFilePath()); + + std::map Result; + std::vector SortedRepls = Repls; + + std::sort(SortedRepls.begin(), SortedRepls.end(), + [](const tooling::Replacement &A, const tooling::Replacement &B) { + return A.getOffset() < B.getOffset(); + }); + + for (const auto &Repl : SortedRepls) { + unsigned StartLine = getLineNumber(FilePath, Repl.getOffset()); + unsigned EndLine = + getLineNumber(FilePath, Repl.getOffset() + Repl.getLength() - 1); + + // process each line + for (unsigned Line = StartLine; Line <= EndLine; ++Line) { + std::string LineContent = getLineString(FilePath, Line).str(); + unsigned LineStartOffset = getLineBeginOffset(FilePath, Line); + unsigned LineEndOffset = LineStartOffset + LineContent.size(); + + // calculate the replacement range within the line + unsigned ReplaceStart = + std::max(Repl.getOffset(), LineStartOffset) - LineStartOffset; + unsigned ReplaceEnd = + std::min(Repl.getOffset() + Repl.getLength(), LineEndOffset) - + LineStartOffset; + + // do replace + if (Line == StartLine) { + LineContent.replace( + ReplaceStart, ReplaceEnd - ReplaceStart, + Repl.getReplacementText().substr(0, ReplaceEnd - ReplaceStart)); + } else if (Line == EndLine) { + unsigned textStart = + Repl.getReplacementText().size() - (ReplaceEnd - ReplaceStart); + LineContent.replace(0, ReplaceEnd, + Repl.getReplacementText().substr(textStart)); + } else { + LineContent = Repl.getReplacementText().substr( + ReplaceStart, LineEndOffset - LineStartOffset); + } + + Result[Line] = LineContent; + } + } + return Result; +} + +std::map +convertReplcementsLineString(const tooling::Replacements &Repls) { + std::vector ReplsVec; + for (const auto &R : Repls) { + ReplsVec.push_back(R); + } + return convertReplcementsLineString(ReplsVec); +} + +std::vector +convertMapToReplacements(const std::map &Map, + const clang::tooling::UnifiedPath &FilePath) { + std::vector Result; + for (const auto &Pair : Map) { + unsigned LineNumber = Pair.first; + StringRef LineContent = Pair.second; + unsigned Offset = getLineBeginOffset(FilePath, LineNumber); + Result.emplace_back(FilePath, Offset, LineContent.size(), + LineContent.str()); + } + return Result; +} + +std::vector +mergeMapsByLine(const std::map &MapA, + const std::map &MapB, + const clang::tooling::UnifiedPath &FilePath) { + auto genReplacement = [&](unsigned LineNumber, + const std::string &LineContent) { + unsigned Offset = getLineBeginOffset(FilePath, LineNumber); + return tooling::Replacement(FilePath.getCanonicalPath(), Offset, + LineContent.size(), LineContent); + }; + + std::vector Result; + auto ItA = MapA.begin(); + auto ItB = MapB.begin(); + + while (ItA != MapA.end() || ItB != MapB.end()) { + if (ItA == MapA.end()) { + Result.push_back(genReplacement(ItB->first, ItB->second)); + ++ItB; + } else if (ItB == MapB.end()) { + Result.push_back(genReplacement(ItA->first, ItA->second)); + ++ItA; + } else if (ItA->first < ItB->first) { + Result.push_back(genReplacement(ItA->first, ItA->second)); + ++ItA; + } else if (ItB->first < ItA->first) { + Result.push_back(genReplacement(ItB->first, ItB->second)); + ++ItB; + } else { + // Conflict line(s) + std::vector ConflictA; + std::vector ConflictB; + unsigned ConflictOffset = ItA->first; + unsigned ConflictLength = 0; + + // Collect continuous conflicting lines + while (ItA != MapA.end() && ItB != MapB.end() && + ItA->first == ItB->first) { + ConflictA.push_back(ItA->second); + ConflictB.push_back(ItB->second); + ++ItA; + ++ItB; + ConflictLength += ItA->second.size(); + } + + // generate merged string + std::string Merged = "<<<<<<<\n"; + for (const auto &L : ConflictA) + Merged += L; + Merged += "=======\n"; + for (const auto &L : ConflictB) + Merged += L; + Merged += ">>>>>>>\n"; + + Result.emplace_back(FilePath.getCanonicalPath(), ConflictOffset, + ConflictLength, Merged); + } + } + return Result; +} + // Repl A // [CUDA code 1] -----------------------------------------> [CUDA code 2] // | / | @@ -136,9 +293,7 @@ groupReplcementsFileByFile( // Repl_C_x will be ignored during this merge. // 2. Shfit Repl_C_y with Repl_A, called Repl_D. // 3. Merge Repl_D and Repl_B. May have conflicts. - -// !!!WIP!!! -std::map +std::map> reMigrationMerge(const GitDiffChanges &Repl_A, const std::vector &Repl_B, const std::vector &Repl_C1, @@ -213,36 +368,40 @@ reMigrationMerge(const GitDiffChanges &Repl_A, // Shift Repl_C_y with Repl_A(ModifiedParts) std::map Repl_D; - for (const auto& Item: Repl_C_y) { + for (const auto &Item : Repl_C_y) { const auto &FilePath = Item.first; const auto &Repls = Item.second; - // Check if the file has modified parts. const auto &It = ModifiedParts.find(FilePath); if (It == ModifiedParts.end()) { Repl_D[FilePath] = Repls; continue; } - Repl_D[FilePath] = calculateUpdatedRanges(It->second, Repls); } - + // Group Repl_B by file - const auto Repl_B_by_file = groupReplcementsFileByFile(Repl_B); + const auto Repl_B_by_file = groupReplcementsByFile(Repl_B); // Merge Repl_D and Repl_B - // 1. we need group the replacements by line number fisrt. For repl in the same line, we need merge them together. - // 2. then we should convert the replacements to a map . We will have 2 maps. - // 3. we need a vector for current file (CUDA code 2) - // 4. merge by line, generate a new map - // 5. convert that map to Replacements. - for (const auto &Repls : Repl_B_by_file) { - - + // 1. we should convert the replacements to a map . We will have 2 maps. + // 2. we need a vector for current file (CUDA code 2) + // 3. merge line by line + std::map> Result; + for (const auto &Pair : Repl_B_by_file) { + std::map ReplBInLines = + convertReplcementsLineString(Pair.second); + if (Repl_D.find(Pair.first) != Repl_D.end()) { + // Merge Repl_D and Repl_B + std::map ReplDInLines = + convertReplcementsLineString(Repl_D[Pair.first]); + Result[Pair.first] = + mergeMapsByLine(ReplBInLines, ReplDInLines, Pair.first); + } else { + // No Repl_D for this file, just add Repl_B. + Result[Pair.first] = Pair.second; + } } - - - std::map Result; return Result; } } // namespace clang::dpct From 00d66d7f6fffc75200f1ee7e6764a257ea26be32 Mon Sep 17 00:00:00 2001 From: "Jiang, Zhiwei" Date: Wed, 4 Jun 2025 15:53:13 +0800 Subject: [PATCH 28/53] fix build Signed-off-by: Jiang, Zhiwei --- clang/lib/DPCT/IncMigration/ReMigration.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/lib/DPCT/IncMigration/ReMigration.cpp b/clang/lib/DPCT/IncMigration/ReMigration.cpp index 602957d046bd..527b7c662d9a 100644 --- a/clang/lib/DPCT/IncMigration/ReMigration.cpp +++ b/clang/lib/DPCT/IncMigration/ReMigration.cpp @@ -190,7 +190,7 @@ convertMapToReplacements(const std::map &Map, unsigned LineNumber = Pair.first; StringRef LineContent = Pair.second; unsigned Offset = getLineBeginOffset(FilePath, LineNumber); - Result.emplace_back(FilePath, Offset, LineContent.size(), + Result.emplace_back(FilePath.getCanonicalPath(), Offset, LineContent.size(), LineContent.str()); } return Result; From f4e07f968d5e1e6601caf48f3f2cb004967e5fb8 Mon Sep 17 00:00:00 2001 From: "Jiang, Zhiwei" Date: Tue, 17 Jun 2025 11:35:26 +0800 Subject: [PATCH 29/53] Add test for calculateUpdatedRanges Signed-off-by: Jiang, Zhiwei --- clang/lib/DPCT/IncMigration/ReMigration.cpp | 59 ++++++++++----- clang/lib/DPCT/IncMigration/ReMigration.h | 4 + clang/unittests/CMakeLists.txt | 62 ++++++++------- clang/unittests/DPCT/CMakeLists.txt | 17 +++++ clang/unittests/DPCT/ReMigrationTest.cpp | 83 +++++++++++++++++++++ 5 files changed, 176 insertions(+), 49 deletions(-) create mode 100644 clang/unittests/DPCT/CMakeLists.txt create mode 100644 clang/unittests/DPCT/ReMigrationTest.cpp diff --git a/clang/lib/DPCT/IncMigration/ReMigration.cpp b/clang/lib/DPCT/IncMigration/ReMigration.cpp index 527b7c662d9a..243dd69c1be6 100644 --- a/clang/lib/DPCT/IncMigration/ReMigration.cpp +++ b/clang/lib/DPCT/IncMigration/ReMigration.cpp @@ -16,6 +16,7 @@ #include "llvm/ADT/StringRef.h" #include "llvm/Support/raw_ostream.h" +#include #include #include @@ -77,21 +78,42 @@ void tryLoadingUpstreamChangesAndUserChanges() { /// Calculate the new Repls of the input \p NewRepl after \p Repls is applied to /// the files. +/// This shfit may have conflicts. +/// Since \p NewRepl (from Repl_C_y) is line based and \p Repls (from Repl_A) is +/// character based, we assume that if there is a conflict, the range from +/// Repl_C_y is always covering the range from Repl_A. Then we just ignore the +/// \p NewRepl (from Repl_C_y) since the old CUDA code is changed, so the +/// migration repl is out-of-date. /// \param Repls Replacements to apply. /// \param NewRepl Replacements before applying \p Repls. /// \return The result Repls. clang::tooling::Replacements calculateUpdatedRanges(const clang::tooling::Replacements &Repls, const clang::tooling::Replacements &NewRepl) { + // Assumption: no overlap in the each groups. clang::tooling::Replacements Result; for (const auto &R : NewRepl) { + // Check if the range (BOffset, EOffset - BOffset) is overlapped with any + // repl in Repls + std::optional MaxNotGreater = std::nullopt; + for (const auto &ExistingR : Repls) { + if (ExistingR.getOffset() <= R.getOffset()) + MaxNotGreater = ExistingR; + else + break; + } + if (MaxNotGreater.has_value()) { + if (MaxNotGreater->getOffset() + MaxNotGreater->getLength() > R.getOffset()) + continue; // has overlap + } + unsigned int BOffset = Repls.getShiftedCodePosition(R.getOffset()); unsigned int EOffset = Repls.getShiftedCodePosition(R.getOffset() + R.getLength()); if (BOffset > EOffset) continue; - (void)Result.add(tooling::Replacement( - R.getFilePath(), BOffset, EOffset - BOffset, R.getReplacementText())); + llvm::cantFail(Result.add(tooling::Replacement( + R.getFilePath(), BOffset, EOffset - BOffset, R.getReplacementText()))); } return Result; } @@ -267,7 +289,7 @@ mergeMapsByLine(const std::map &MapA, // | / | // | Repl C2 Repl D / | // | / | -// V shift V merge | +// V shift (may have conlifct) V merge | // [SYCL code 1.1] ----------------> [SYCL code 1.1] ----------> | // (based on CUDA code 1) (based on CUDA code 2) | // V @@ -320,19 +342,18 @@ reMigrationMerge(const GitDiffChanges &Repl_A, DeletedParts[Hunk.getFilePath().str()][Hunk.getOffset()] = Hunk.getLength(); } - (void)ModifiedParts[Hunk.getFilePath().str()].add( - tooling::Replacement(Hunk.getFilePath().str(), - Hunk.getOffset(), Hunk.getLength(), - Hunk.getReplacementText())); + llvm::cantFail(ModifiedParts[Hunk.getFilePath().str()].add( + tooling::Replacement(Hunk.getFilePath().str(), Hunk.getOffset(), + Hunk.getLength(), Hunk.getReplacementText()))); } for (const auto &Hunk : Repl_A.MoveFileHunks) { if (Hunk.getLength() != 0 && Hunk.getReplacementText().size() == 0) { - DeletedParts[Hunk.getFilePath().str()][Hunk.getOffset()] = Hunk.getLength(); + DeletedParts[Hunk.getFilePath().str()][Hunk.getOffset()] = + Hunk.getLength(); } - (void)ModifiedParts[Hunk.getFilePath().str()].add( - tooling::Replacement(Hunk.getFilePath().str(), - Hunk.getOffset(), Hunk.getLength(), - Hunk.getReplacementText())); + llvm::cantFail(ModifiedParts[Hunk.getFilePath().str()].add( + tooling::Replacement(Hunk.getFilePath().str(), Hunk.getOffset(), + Hunk.getLength(), Hunk.getReplacementText()))); } for (const auto &Hunk : Repl_A.DeleteFileHunks) { DeletedParts[Hunk.getOldFilePath()] = std::map(); @@ -345,10 +366,9 @@ reMigrationMerge(const GitDiffChanges &Repl_A, // So here assume there is no overlap between delete hunks and replacements. const auto &It = DeletedParts.find(Repl.getFilePath().str()); if (It == DeletedParts.end()) { - (void)Repl_C_y[Repl.getFilePath().str()].add( - tooling::Replacement(Repl.getFilePath().str(), - Repl.getOffset(), Repl.getLength(), - Repl.getReplacementText())); + llvm::cantFail(Repl_C_y[Repl.getFilePath().str()].add( + tooling::Replacement(Repl.getFilePath().str(), Repl.getOffset(), + Repl.getLength(), Repl.getReplacementText()))); continue; } @@ -360,10 +380,9 @@ reMigrationMerge(const GitDiffChanges &Repl_A, break; } } - (void)Repl_C_y[Repl.getFilePath().str()].add( - tooling::Replacement(Repl.getFilePath().str(), - Repl.getOffset(), Repl.getLength(), - Repl.getReplacementText())); + llvm::cantFail(Repl_C_y[Repl.getFilePath().str()].add( + tooling::Replacement(Repl.getFilePath().str(), Repl.getOffset(), + Repl.getLength(), Repl.getReplacementText()))); } // Shift Repl_C_y with Repl_A(ModifiedParts) diff --git a/clang/lib/DPCT/IncMigration/ReMigration.h b/clang/lib/DPCT/IncMigration/ReMigration.h index cc0154883c73..332b61fd8f1f 100644 --- a/clang/lib/DPCT/IncMigration/ReMigration.h +++ b/clang/lib/DPCT/IncMigration/ReMigration.h @@ -84,6 +84,10 @@ struct GitDiffChanges { }; GitDiffChanges &getUpstreamChanges(); GitDiffChanges &getUserChanges(); + +clang::tooling::Replacements +calculateUpdatedRanges(const clang::tooling::Replacements &Repls, + const clang::tooling::Replacements &NewRepl); } // namespace dpct } // namespace clang diff --git a/clang/unittests/CMakeLists.txt b/clang/unittests/CMakeLists.txt index 85d265426ec8..1e23a76a88af 100644 --- a/clang/unittests/CMakeLists.txt +++ b/clang/unittests/CMakeLists.txt @@ -23,32 +23,36 @@ function(add_clang_unittest test_dirname) add_unittest(ClangUnitTests ${test_dirname} ${ARGN}) endfunction() -add_subdirectory(Basic) -add_subdirectory(Lex) -add_subdirectory(Driver) -if(CLANG_ENABLE_STATIC_ANALYZER) - add_subdirectory(Analysis) - add_subdirectory(StaticAnalyzer) -endif() -add_subdirectory(ASTMatchers) -add_subdirectory(AST) -add_subdirectory(CrossTU) -add_subdirectory(Tooling) -add_subdirectory(Format) -add_subdirectory(Frontend) -add_subdirectory(Rewrite) -add_subdirectory(Sema) -add_subdirectory(CodeGen) -if(HAVE_CLANG_REPL_SUPPORT) - add_subdirectory(Interpreter) -endif() -# FIXME: libclang unit tests are disabled on Windows due -# to failures, mostly in libclang.VirtualFileOverlay_*. -if(NOT WIN32 AND CLANG_TOOL_LIBCLANG_BUILD) - add_subdirectory(libclang) -endif() -add_subdirectory(DirectoryWatcher) -add_subdirectory(Index) -add_subdirectory(InstallAPI) -add_subdirectory(Serialization) -add_subdirectory(Support) +# SYCLomatic_CUSTOMIZATION begin +add_subdirectory(DPCT) +# SYCLomatic_CUSTOMIZATION else +# add_subdirectory(Basic) +# add_subdirectory(Lex) +# add_subdirectory(Driver) +# if(CLANG_ENABLE_STATIC_ANALYZER) +# add_subdirectory(Analysis) +# add_subdirectory(StaticAnalyzer) +# endif() +# add_subdirectory(ASTMatchers) +# add_subdirectory(AST) +# add_subdirectory(CrossTU) +# add_subdirectory(Tooling) +# add_subdirectory(Format) +# add_subdirectory(Frontend) +# add_subdirectory(Rewrite) +# add_subdirectory(Sema) +# add_subdirectory(CodeGen) +# if(HAVE_CLANG_REPL_SUPPORT) +# add_subdirectory(Interpreter) +# endif() +# # FIXME: libclang unit tests are disabled on Windows due +# # to failures, mostly in libclang.VirtualFileOverlay_*. +# if(NOT WIN32 AND CLANG_TOOL_LIBCLANG_BUILD) +# add_subdirectory(libclang) +# endif() +# add_subdirectory(DirectoryWatcher) +# add_subdirectory(Index) +# add_subdirectory(InstallAPI) +# add_subdirectory(Serialization) +# add_subdirectory(Support) +# SYCLomatic_CUSTOMIZATION end diff --git a/clang/unittests/DPCT/CMakeLists.txt b/clang/unittests/DPCT/CMakeLists.txt new file mode 100644 index 000000000000..2e2f8f04df62 --- /dev/null +++ b/clang/unittests/DPCT/CMakeLists.txt @@ -0,0 +1,17 @@ +set(LLVM_LINK_COMPONENTS + Support + ) + +add_clang_unittest(DpctTests + ReMigrationTest.cpp + ) + +target_link_libraries(DpctTests + PRIVATE + DPCT + clangBasic + clangFrontend + clangRewrite + clangTooling + clangToolingCore + ) diff --git a/clang/unittests/DPCT/ReMigrationTest.cpp b/clang/unittests/DPCT/ReMigrationTest.cpp new file mode 100644 index 000000000000..4dcf99ec94bc --- /dev/null +++ b/clang/unittests/DPCT/ReMigrationTest.cpp @@ -0,0 +1,83 @@ +#include "../../lib/DPCT/IncMigration/ReMigration.h" +#include "gtest/gtest.h" + +using namespace llvm; +using namespace clang::tooling; +using namespace clang::dpct; + +class ReMigrationTest : public ::testing::Test { +protected: + void SetUp() override {} + void TearDown() override {} +}; + +TEST_F(ReMigrationTest, calculateUpdatedRanges) { + // clang-format off + // Old base: +/* +aaa bb ccc +aaa bb ccc +aaa bb ccc +aaa bb ccc +aaa bb ccc +aaa bb ccc +aaa bb ccc +*/ + // Old migrated: +/* +aaa zzz ccc +ppp aaa bb ccc +aaa yyy ccc +aaa bb ccc +aaxxx bb ccc +aaa bb ccc +qqq aaa bb ccc +*/ + // New base: +/* +aaa bb ccc +Hi bb Everyone +aaa bb ccc +aaa bb Everyone +Hi Hello Everyone +aaa Hi bb ccc +aaa bb ccc +*/ + // clang-format on + + Replacements NewRepl; + Replacements Repls; + Replacements Expected; + + llvm::cantFail(NewRepl.add(Replacement("file1.cpp", 4, 2, "zzz"))); + llvm::cantFail(NewRepl.add(Replacement("file1.cpp", 11, 0, "ppp "))); + llvm::cantFail(NewRepl.add(Replacement("file1.cpp", 26, 2, "yyy"))); + llvm::cantFail(NewRepl.add(Replacement("file1.cpp", 46, 1, "xxx"))); + llvm::cantFail(NewRepl.add(Replacement("file1.cpp", 65, 1, "\nqqq"))); + + llvm::cantFail( + Repls.add(Replacement("file1.cpp", 11, 11, "Hi bb Everyone\n"))); + llvm::cantFail( + Repls.add(Replacement("file1.cpp", 33, 11, "aaa bb Everyone\n"))); + llvm::cantFail( + Repls.add(Replacement("file1.cpp", 44, 11, "Hi Hello Everyone\n"))); + llvm::cantFail( + Repls.add(Replacement("file1.cpp", 55, 11, "aaa Hi bb ccc\n"))); + + llvm::cantFail(Expected.add(Replacement("file1.cpp", 4, 2, "zzz"))); + llvm::cantFail(Expected.add(Replacement("file1.cpp", 30, 2, "yyy"))); + + Replacements Result = calculateUpdatedRanges(Repls, NewRepl); + ASSERT_EQ(Expected.size(), Result.size()); + size_t Num = Expected.size(); + auto ExpectedIt = Expected.begin(); + auto ResultIt = Result.begin(); + for (size_t i = 0; i < Num; ++i) { + EXPECT_EQ(ExpectedIt->getFilePath(), ResultIt->getFilePath()); + EXPECT_EQ(ExpectedIt->getOffset(), ResultIt->getOffset()); + EXPECT_EQ(ExpectedIt->getLength(), ResultIt->getLength()); + EXPECT_EQ(ExpectedIt->getReplacementText(), ResultIt->getReplacementText()); + ExpectedIt++; + ResultIt++; + } +} From 005db06bb33b5f402ddeb5e4230b47fa9d62cd98 Mon Sep 17 00:00:00 2001 From: "Jiang, Zhiwei" Date: Wed, 18 Jun 2025 08:21:08 +0800 Subject: [PATCH 30/53] Add test for groupReplcementsByFile and convertReplcementsLineString Signed-off-by: Jiang, Zhiwei --- clang/lib/DPCT/IncMigration/ReMigration.cpp | 206 ++++++++++++++------ clang/lib/DPCT/IncMigration/ReMigration.h | 14 ++ clang/unittests/DPCT/ReMigrationTest.cpp | 141 ++++++++++++++ 3 files changed, 304 insertions(+), 57 deletions(-) diff --git a/clang/lib/DPCT/IncMigration/ReMigration.cpp b/clang/lib/DPCT/IncMigration/ReMigration.cpp index 243dd69c1be6..163e09f5bfbc 100644 --- a/clang/lib/DPCT/IncMigration/ReMigration.cpp +++ b/clang/lib/DPCT/IncMigration/ReMigration.cpp @@ -9,6 +9,7 @@ #include "ReMigration.h" #include "AnalysisInfo.h" #include "ExternalReplacement.h" +#include "TextModification.h" #include "clang/AST/Expr.h" #include "clang/Tooling/Core/Replacement.h" @@ -20,6 +21,14 @@ #include #include +std::optional< + std::function> + getLineStringHook = std::nullopt; +std::optional> + getLineNumberHook = std::nullopt; +std::optional> + getLineBeginOffsetHook = std::nullopt; + namespace clang::dpct { static GitDiffChanges UpstreamChanges; static GitDiffChanges UserChanges; @@ -76,6 +85,40 @@ void tryLoadingUpstreamChangesAndUserChanges() { } } +static StringRef getLineString(clang::tooling::UnifiedPath FilePath, + unsigned LineNumber) { +#ifndef NDEBUG + if (getLineStringHook.has_value()) { + return getLineStringHook.value()(FilePath, LineNumber); + } +#endif + auto FileInfo = DpctGlobalInfo::getInstance().insertFile(FilePath); + StringRef Line = FileInfo->getLineString(LineNumber); + return Line; +} + +static unsigned getLineNumber(clang::tooling::UnifiedPath FilePath, + unsigned Offset) { +#ifndef NDEBUG + if (getLineNumberHook.has_value()) { + return getLineNumberHook.value()(FilePath, Offset); + } +#endif + auto FileInfo = DpctGlobalInfo::getInstance().insertFile(FilePath); + return FileInfo->getLineNumber(Offset); +} + +static unsigned getLineBeginOffset(clang::tooling::UnifiedPath FilePath, + unsigned LineNumber) { +#ifndef NDEBUG + if (getLineBeginOffsetHook.has_value()) { + return getLineBeginOffsetHook.value()(FilePath, LineNumber); + } +#endif + auto FileInfo = DpctGlobalInfo::getInstance().insertFile(FilePath); + return FileInfo->getLineInfo(LineNumber).Offset; +} + /// Calculate the new Repls of the input \p NewRepl after \p Repls is applied to /// the files. /// This shfit may have conflicts. @@ -128,69 +171,118 @@ groupReplcementsByFile( return Result; } -StringRef getLineString(clang::tooling::UnifiedPath FilePath, - unsigned LineNumber) { - auto FileInfo = DpctGlobalInfo::getInstance().insertFile(FilePath); - StringRef Line = FileInfo->getLineString(LineNumber); - return Line; -} -unsigned getLineNumber(clang::tooling::UnifiedPath FilePath, unsigned Offset) { - auto FileInfo = DpctGlobalInfo::getInstance().insertFile(FilePath); - return FileInfo->getLineNumber(Offset); -} -unsigned getLineBeginOffset(clang::tooling::UnifiedPath FilePath, - unsigned LineNumber) { - auto FileInfo = DpctGlobalInfo::getInstance().insertFile(FilePath); - return FileInfo->getLineInfo(LineNumber).Offset; +// // This function keep the NL in each string (if has) +// std::vector splitStringByNL(const std::string &Str) { +// std::vector Result; +// if (Str.empty()) +// return Result; +// size_t Start = 0; +// bool InQuotes = false; +// for (size_t i = 0; i < Str.length(); ++i) { +// if (Str[i] == '"') { +// InQuotes = !InQuotes; +// } else if (Str[i] == '\n' && !InQuotes) { +// Result.push_back(Str.substr(Start, i - Start + 1)); +// Start = i + 1; +// } +// } +// if (Start < Str.length()) { +// Result.push_back(Str.substr(Start)); +// } +// return Result; +// } + +// If repl range is cross lines, we treat the \n itself belongs to current line. +// Example: +// aaabbbccc +// dddeeefff +// ggghhhiii +// +// Original repl: +// (ccc\ndddeeefff\nggg) =>(jjj\nkkk) +// +// Splitted repls: +// (ccc\n) =>(jjj\nkkk) +// (dddeeefff\n) => "" +// (ggg) => "" +std::vector splitReplInOrderToNotCrossLines( + const std::vector &InRepls) { + std::string FilePath = InRepls[0].getFilePath().str(); + std::vector Result; + + for (const auto &Repl : InRepls) { + unsigned StartOffset = Repl.getOffset(); + unsigned EndOffset = StartOffset + Repl.getLength(); + unsigned StartLine = getLineNumber(FilePath, StartOffset); + unsigned EndLine = getLineNumber(FilePath, EndOffset); + + if (StartLine == EndLine) { + // Single line replacement + Result.push_back(Repl); + continue; + } + + // Cross-line replacement + unsigned CurrentOffset = StartOffset; + + // The first line + unsigned LineEndOffset = getLineBeginOffset(FilePath, StartLine + 1); + unsigned FirstLineLength = LineEndOffset - StartOffset; + Result.emplace_back(FilePath, CurrentOffset, FirstLineLength, + Repl.getReplacementText()); + CurrentOffset += FirstLineLength; + + // middle lines + for (unsigned Line = StartLine + 1; Line < EndLine; ++Line) { + LineEndOffset = getLineBeginOffset(FilePath, Line + 1); + unsigned lineLength = LineEndOffset - CurrentOffset; + Result.emplace_back(Repl.getFilePath(), CurrentOffset, lineLength, ""); + CurrentOffset += lineLength; + } + + // The last line + unsigned LastLineLength = EndOffset - CurrentOffset; + if (LastLineLength > 0) { + Result.emplace_back(Repl.getFilePath(), CurrentOffset, LastLineLength, + ""); + } + } + + return Result; } std::map -convertReplcementsLineString(const std::vector &Repls) { - tooling::UnifiedPath FilePath(Repls[0].getFilePath()); +convertReplcementsLineString(const std::vector &InRepls) { + std::vector Replacements = + splitReplInOrderToNotCrossLines(InRepls); + tooling::UnifiedPath FilePath(InRepls[0].getFilePath()); + + // group replacement by line + std::map> ReplacementsByLine; + for (const auto &Repl : Replacements) { + unsigned LineNum = getLineNumber(FilePath, Repl.getOffset()); + ReplacementsByLine[LineNum].push_back(Repl); + } + // process each line std::map Result; - std::vector SortedRepls = Repls; - - std::sort(SortedRepls.begin(), SortedRepls.end(), - [](const tooling::Replacement &A, const tooling::Replacement &B) { - return A.getOffset() < B.getOffset(); - }); - - for (const auto &Repl : SortedRepls) { - unsigned StartLine = getLineNumber(FilePath, Repl.getOffset()); - unsigned EndLine = - getLineNumber(FilePath, Repl.getOffset() + Repl.getLength() - 1); - - // process each line - for (unsigned Line = StartLine; Line <= EndLine; ++Line) { - std::string LineContent = getLineString(FilePath, Line).str(); - unsigned LineStartOffset = getLineBeginOffset(FilePath, Line); - unsigned LineEndOffset = LineStartOffset + LineContent.size(); - - // calculate the replacement range within the line - unsigned ReplaceStart = - std::max(Repl.getOffset(), LineStartOffset) - LineStartOffset; - unsigned ReplaceEnd = - std::min(Repl.getOffset() + Repl.getLength(), LineEndOffset) - - LineStartOffset; - - // do replace - if (Line == StartLine) { - LineContent.replace( - ReplaceStart, ReplaceEnd - ReplaceStart, - Repl.getReplacementText().substr(0, ReplaceEnd - ReplaceStart)); - } else if (Line == EndLine) { - unsigned textStart = - Repl.getReplacementText().size() - (ReplaceEnd - ReplaceStart); - LineContent.replace(0, ReplaceEnd, - Repl.getReplacementText().substr(textStart)); - } else { - LineContent = Repl.getReplacementText().substr( - ReplaceStart, LineEndOffset - LineStartOffset); - } - - Result[Line] = LineContent; + for (auto &[LineNum, Repls] : ReplacementsByLine) { + std::sort(Repls.begin(), Repls.end()); + + std::string OriginalLineStr = getLineString(FilePath, LineNum).str(); + unsigned LineStartOffset = getLineBeginOffset(FilePath, LineNum); + std::string NewLineStr; + unsigned Pos = 0; + for (const auto &Repl : Repls) { + unsigned StrOffset = Repl.getOffset() - LineStartOffset; + NewLineStr += OriginalLineStr.substr(Pos, StrOffset - Pos); + NewLineStr += Repl.getReplacementText().str(); + Pos = StrOffset + Repl.getLength(); } + std::cout << "OriginalLineStr:" << OriginalLineStr << "!!!" << std::endl; + std::cout << "Pos:" << Pos << std::endl; + NewLineStr += OriginalLineStr.substr(Pos); + Result[LineNum] = NewLineStr; } return Result; } diff --git a/clang/lib/DPCT/IncMigration/ReMigration.h b/clang/lib/DPCT/IncMigration/ReMigration.h index 332b61fd8f1f..354c7eb5994f 100644 --- a/clang/lib/DPCT/IncMigration/ReMigration.h +++ b/clang/lib/DPCT/IncMigration/ReMigration.h @@ -12,6 +12,16 @@ #include "clang/Tooling/Core/Replacement.h" #include "clang/Tooling/ReplacementsYaml.h" +extern std::optional< + std::function> + getLineStringHook; +extern std::optional< + std::function> + getLineNumberHook; +extern std::optional< + std::function> + getLineBeginOffsetHook; + namespace clang { namespace dpct { class Hunk { @@ -88,6 +98,10 @@ GitDiffChanges &getUserChanges(); clang::tooling::Replacements calculateUpdatedRanges(const clang::tooling::Replacements &Repls, const clang::tooling::Replacements &NewRepl); +std::map> +groupReplcementsByFile(const std::vector &Repls); +std::map +convertReplcementsLineString(const std::vector &Repls); } // namespace dpct } // namespace clang diff --git a/clang/unittests/DPCT/ReMigrationTest.cpp b/clang/unittests/DPCT/ReMigrationTest.cpp index 4dcf99ec94bc..99d2a239078a 100644 --- a/clang/unittests/DPCT/ReMigrationTest.cpp +++ b/clang/unittests/DPCT/ReMigrationTest.cpp @@ -1,10 +1,46 @@ #include "../../lib/DPCT/IncMigration/ReMigration.h" +#include "clang/Tooling/Core/Replacement.h" +#include "clang/Tooling/Core/UnifiedPath.h" #include "gtest/gtest.h" +#include using namespace llvm; using namespace clang::tooling; using namespace clang::dpct; +namespace { + // clang-format off +/* +aaa bb ccc +aaa bb ccc +aaa bb ccc +aaa bb ccc +aaa bb ccc +aaa bb ccc +aaa bb ccc +*/ + // clang-format on +StringRef getLineStringUnittest(clang::tooling::UnifiedPath FilePath, + unsigned LineNumber) { + static std::string S = "aaa bb ccc\n"; + return StringRef(S); +} +unsigned getLineNumberUnittest(clang::tooling::UnifiedPath FilePath, + unsigned Offset) { + static std::vector LineOffsets = {0, 11, 22, 33, 44, 55, 66, 77, 88, 99}; + auto Iter = std::upper_bound(LineOffsets.begin(), LineOffsets.end(), Offset); + if (Iter == LineOffsets.end()) + return LineOffsets.size(); + return std::distance(LineOffsets.begin(), Iter); +} +unsigned getLineBeginOffsetUnittest(clang::tooling::UnifiedPath FilePath, + unsigned LineNumber) { + static std::unordered_map LineOffsets = { + {1, 0}, {2, 11}, {3, 22}, {4, 33}, {5, 44}, {6, 55}, {7, 66}, {8, 77}, {9, 88}, {10, 99}}; + return LineOffsets[LineNumber]; +} +} // namespace + class ReMigrationTest : public ::testing::Test { protected: void SetUp() override {} @@ -81,3 +117,108 @@ aaa bb ccc ResultIt++; } } + +TEST_F(ReMigrationTest, groupReplcementsByFile) { + std::string FilePath1 = + clang::tooling::UnifiedPath("file1.cpp").getCanonicalPath().str(); + std::string FilePath2 = + clang::tooling::UnifiedPath("file2.cpp").getCanonicalPath().str(); + std::string FilePath3 = + clang::tooling::UnifiedPath("file3.cpp").getCanonicalPath().str(); + + std::vector Repls = {{FilePath1, 0, 0, ""}, + {FilePath2, 3, 0, ""}, + {FilePath1, 1, 0, ""}, + {FilePath3, 4, 0, ""}, + {FilePath2, 2, 0, ""}}; + std::map> Expected = { + {FilePath1, + { + {FilePath1, 0, 0, ""}, + {FilePath1, 1, 0, ""}, + }}, + {FilePath2, + { + {FilePath2, 2, 0, ""}, + {FilePath2, 3, 0, ""}, + }}, + {FilePath3, {{FilePath3, 4, 0, ""}}}}; + + auto Result = groupReplcementsByFile(Repls); + EXPECT_EQ(Expected.size(), Result.size()); + + size_t Num = Expected.size(); + auto ExpectedIt = Expected.begin(); + auto ResultIt = Result.begin(); + for (size_t i = 0; i < Num; ++i) { + EXPECT_EQ(ExpectedIt->first, ResultIt->first); + EXPECT_EQ(ExpectedIt->second.size(), ResultIt->second.size()); + size_t SubNum = ExpectedIt->second.size(); + std::sort(ExpectedIt->second.begin(), ExpectedIt->second.end()); + std::sort(ResultIt->second.begin(), ResultIt->second.end()); + for (size_t j = 0; j < SubNum; ++j) { + EXPECT_EQ(ExpectedIt->second[j].getFilePath(), + ResultIt->second[j].getFilePath()); + EXPECT_EQ(ExpectedIt->second[j].getOffset(), + ResultIt->second[j].getOffset()); + EXPECT_EQ(ExpectedIt->second[j].getLength(), + ResultIt->second[j].getLength()); + EXPECT_EQ(ExpectedIt->second[j].getReplacementText(), + ResultIt->second[j].getReplacementText()); + } + ExpectedIt++; + ResultIt++; + } +} + +TEST_F(ReMigrationTest, convertReplcementsLineString) { + // clang-format off + // Old base: +/* +aaa bb ccc +aaa bb ccc +aaa bb ccc +aaa bb ccc +aaa bb ccc +aaa bb ccc +aaa bb ccc +aaa bb ccc +aaa bb ccc +aaa bb ccc +*/ + // After appling repls: +/* +aaa zzz ccc +ppp aaa bb ccc +aaa bb ccc +aaa yyy ccc +aaa bb ccc +aaa bb ccq +qqqaa bb ccc +aaa bb ddd + +eee bb ccc +*/ + // clang-format on + getLineStringHook = getLineStringUnittest; + getLineNumberHook = getLineNumberUnittest; + getLineBeginOffsetHook = getLineBeginOffsetUnittest; + std::vector Repls = { + Replacement("file1.cpp", 4, 2, "zzz"), + Replacement("file1.cpp", 64, 3, "q\nqqq"), + Replacement("file1.cpp", 33, 11, "aaa yyy ccc\n"), + Replacement("file1.cpp", 11, 0, "ppp "), + Replacement("file1.cpp", 84, 18, "ddd\neee") + }; + std::map Expected = {{1, "aaa zzz ccc\n"}, + {2, "ppp aaa bb ccc\n"}, + {4, "aaa yyy ccc\n"}, + {6, "aaa bb ccq\nqqq"}, + {7, "aa bb ccc\n"}, + {8, "aaa bb ddd\neee"}, + {9, ""}, + {10, " bb ccc\n"} + }; + auto Result = convertReplcementsLineString(Repls); + EXPECT_EQ(Expected, Result); +} From cd62439a84d609d338190047b8d6d6ae5c6db811 Mon Sep 17 00:00:00 2001 From: "Jiang, Zhiwei" Date: Thu, 19 Jun 2025 13:12:53 +0800 Subject: [PATCH 31/53] add test for splitReplInOrderToNotCrossLines Signed-off-by: Jiang, Zhiwei --- clang/lib/DPCT/IncMigration/ReMigration.cpp | 2 +- clang/lib/DPCT/IncMigration/ReMigration.h | 2 ++ clang/unittests/DPCT/ReMigrationTest.cpp | 39 +++++++++++++++++++++ 3 files changed, 42 insertions(+), 1 deletion(-) diff --git a/clang/lib/DPCT/IncMigration/ReMigration.cpp b/clang/lib/DPCT/IncMigration/ReMigration.cpp index 163e09f5bfbc..9ffe16ec2903 100644 --- a/clang/lib/DPCT/IncMigration/ReMigration.cpp +++ b/clang/lib/DPCT/IncMigration/ReMigration.cpp @@ -287,7 +287,7 @@ convertReplcementsLineString(const std::vector &InRepls) { return Result; } -std::map +static std::map convertReplcementsLineString(const tooling::Replacements &Repls) { std::vector ReplsVec; for (const auto &R : Repls) { diff --git a/clang/lib/DPCT/IncMigration/ReMigration.h b/clang/lib/DPCT/IncMigration/ReMigration.h index 354c7eb5994f..3284f101b43a 100644 --- a/clang/lib/DPCT/IncMigration/ReMigration.h +++ b/clang/lib/DPCT/IncMigration/ReMigration.h @@ -100,6 +100,8 @@ calculateUpdatedRanges(const clang::tooling::Replacements &Repls, const clang::tooling::Replacements &NewRepl); std::map> groupReplcementsByFile(const std::vector &Repls); +std::vector splitReplInOrderToNotCrossLines( + const std::vector &InRepls); std::map convertReplcementsLineString(const std::vector &Repls); } // namespace dpct diff --git a/clang/unittests/DPCT/ReMigrationTest.cpp b/clang/unittests/DPCT/ReMigrationTest.cpp index 99d2a239078a..c9745a947b84 100644 --- a/clang/unittests/DPCT/ReMigrationTest.cpp +++ b/clang/unittests/DPCT/ReMigrationTest.cpp @@ -171,6 +171,45 @@ TEST_F(ReMigrationTest, groupReplcementsByFile) { } } +TEST_F(ReMigrationTest, splitReplInOrderToNotCrossLines) { + // clang-format off + // Old base: +/* +Example: +aaabbbbccc +dddeeeefff +ggghhhhiii +zzzzzzzzzz +yyyyyyyyyy +// +// Original repl: +// (ccc\ndddeeeefff\nggg) =>(jjj\nkkk) +// (zz\nyy) =>(xx) +// +// Splitted repls: +// (ccc\n) =>(jjj\nkkk) +// (dddeeeefff\n) => "" +// (ggg) => "" +// (zz\n) => (xx) +// (yy) => "" +eee bb ccc +*/ + // clang-format on + getLineStringHook = getLineStringUnittest; + getLineNumberHook = getLineNumberUnittest; + getLineBeginOffsetHook = getLineBeginOffsetUnittest; + std::vector Repls = {Replacement("file1.cpp", 7, 18, "jjj\nkkk"), + Replacement("file1.cpp", 41, 5, "xx")}; + std::vector Expected = { + Replacement("file1.cpp", 7, 4, "jjj\nkkk"), + Replacement("file1.cpp", 11, 11, ""), Replacement("file1.cpp", 22, 3, ""), + Replacement("file1.cpp", 41, 3, "xx"), + Replacement("file1.cpp", 44, 2, "")}; + auto Result = splitReplInOrderToNotCrossLines(Repls); + std::sort(Result.begin(), Result.end()); + EXPECT_EQ(Expected, Result); +} + TEST_F(ReMigrationTest, convertReplcementsLineString) { // clang-format off // Old base: From 9dfe19bc635177623c486af4ba762776b048ecf3 Mon Sep 17 00:00:00 2001 From: "Jiang, Zhiwei" Date: Thu, 19 Jun 2025 14:08:58 +0800 Subject: [PATCH 32/53] add test for mergeMapsByLine Signed-off-by: Jiang, Zhiwei --- clang/lib/DPCT/IncMigration/ReMigration.cpp | 44 +++------------------ clang/lib/DPCT/IncMigration/ReMigration.h | 4 ++ clang/unittests/DPCT/ReMigrationTest.cpp | 39 ++++++++++++++++++ 3 files changed, 48 insertions(+), 39 deletions(-) diff --git a/clang/lib/DPCT/IncMigration/ReMigration.cpp b/clang/lib/DPCT/IncMigration/ReMigration.cpp index 9ffe16ec2903..f16e28d7db0e 100644 --- a/clang/lib/DPCT/IncMigration/ReMigration.cpp +++ b/clang/lib/DPCT/IncMigration/ReMigration.cpp @@ -171,27 +171,6 @@ groupReplcementsByFile( return Result; } -// // This function keep the NL in each string (if has) -// std::vector splitStringByNL(const std::string &Str) { -// std::vector Result; -// if (Str.empty()) -// return Result; -// size_t Start = 0; -// bool InQuotes = false; -// for (size_t i = 0; i < Str.length(); ++i) { -// if (Str[i] == '"') { -// InQuotes = !InQuotes; -// } else if (Str[i] == '\n' && !InQuotes) { -// Result.push_back(Str.substr(Start, i - Start + 1)); -// Start = i + 1; -// } -// } -// if (Start < Str.length()) { -// Result.push_back(Str.substr(Start)); -// } -// return Result; -// } - // If repl range is cross lines, we treat the \n itself belongs to current line. // Example: // aaabbbccc @@ -296,20 +275,6 @@ convertReplcementsLineString(const tooling::Replacements &Repls) { return convertReplcementsLineString(ReplsVec); } -std::vector -convertMapToReplacements(const std::map &Map, - const clang::tooling::UnifiedPath &FilePath) { - std::vector Result; - for (const auto &Pair : Map) { - unsigned LineNumber = Pair.first; - StringRef LineContent = Pair.second; - unsigned Offset = getLineBeginOffset(FilePath, LineNumber); - Result.emplace_back(FilePath.getCanonicalPath(), Offset, LineContent.size(), - LineContent.str()); - } - return Result; -} - std::vector mergeMapsByLine(const std::map &MapA, const std::map &MapB, @@ -317,8 +282,9 @@ mergeMapsByLine(const std::map &MapA, auto genReplacement = [&](unsigned LineNumber, const std::string &LineContent) { unsigned Offset = getLineBeginOffset(FilePath, LineNumber); - return tooling::Replacement(FilePath.getCanonicalPath(), Offset, - LineContent.size(), LineContent); + unsigned StrLen = getLineString(FilePath, LineNumber).size(); + return tooling::Replacement(FilePath.getCanonicalPath(), Offset, StrLen, + LineContent); }; std::vector Result; @@ -342,7 +308,7 @@ mergeMapsByLine(const std::map &MapA, // Conflict line(s) std::vector ConflictA; std::vector ConflictB; - unsigned ConflictOffset = ItA->first; + unsigned ConflictOffset = getLineBeginOffset(FilePath, ItA->first); unsigned ConflictLength = 0; // Collect continuous conflicting lines @@ -352,7 +318,7 @@ mergeMapsByLine(const std::map &MapA, ConflictB.push_back(ItB->second); ++ItA; ++ItB; - ConflictLength += ItA->second.size(); + ConflictLength += getLineString(FilePath, ItA->first).size(); } // generate merged string diff --git a/clang/lib/DPCT/IncMigration/ReMigration.h b/clang/lib/DPCT/IncMigration/ReMigration.h index 3284f101b43a..6f20b0e56035 100644 --- a/clang/lib/DPCT/IncMigration/ReMigration.h +++ b/clang/lib/DPCT/IncMigration/ReMigration.h @@ -104,6 +104,10 @@ std::vector splitReplInOrderToNotCrossLines( const std::vector &InRepls); std::map convertReplcementsLineString(const std::vector &Repls); +std::vector +mergeMapsByLine(const std::map &MapA, + const std::map &MapB, + const clang::tooling::UnifiedPath &FilePath); } // namespace dpct } // namespace clang diff --git a/clang/unittests/DPCT/ReMigrationTest.cpp b/clang/unittests/DPCT/ReMigrationTest.cpp index c9745a947b84..439299a75d4c 100644 --- a/clang/unittests/DPCT/ReMigrationTest.cpp +++ b/clang/unittests/DPCT/ReMigrationTest.cpp @@ -261,3 +261,42 @@ eee bb ccc auto Result = convertReplcementsLineString(Repls); EXPECT_EQ(Expected, Result); } + +TEST_F(ReMigrationTest, mergeMapsByLine) { + getLineBeginOffsetHook = getLineBeginOffsetUnittest; + getLineStringHook = getLineStringUnittest; + std::map MapA = {{1, "zzzzz\n"}, {3, "xxxx1\n"}, + {4, "wwww1\n"}, {5, "vvvvv\n"}, + {7, "ppppp\n"}, {9, "iiijjjkkk\n"}}; + std::map MapB = { + {2, "yyyyy\n"}, {3, "xxxx2\n"}, {4, "wwww2\n"}, {7, ""}, {8, "qqqqq"}}; + UnifiedPath FilePath("test.cu"); + auto Result = mergeMapsByLine(MapA, MapB, FilePath); + std::sort(Result.begin(), Result.end()); + + std::vector Expected = { + Replacement("test.cu", 0, 11, "zzzzz\n"), + Replacement("test.cu", 11, 11, "yyyyy\n"), + Replacement("test.cu", 22, 22, + "<<<<<<<\nxxxx1\nwwww1\n=======\nxxxx2\nwwww2\n>>>>>>>\n"), + Replacement("test.cu", 44, 11, "vvvvv\n"), + Replacement("test.cu", 66, 11, "<<<<<<<\nppppp\n=======\n>>>>>>>\n"), + Replacement("test.cu", 77, 11, "qqqqq"), + Replacement("test.cu", 88, 11, "iiijjjkkk\n"), + }; + + ASSERT_EQ(Expected.size(), Result.size()); + size_t Num = Expected.size(); + auto ExpectedIt = Expected.begin(); + auto ResultIt = Result.begin(); + for (size_t i = 0; i < Num; ++i) { + EXPECT_EQ(ExpectedIt->getFilePath(), ResultIt->getFilePath()); + EXPECT_EQ(ExpectedIt->getOffset(), ResultIt->getOffset()); + EXPECT_EQ(ExpectedIt->getLength(), ResultIt->getLength()); + EXPECT_EQ(ExpectedIt->getReplacementText(), ResultIt->getReplacementText()); + ExpectedIt++; + ResultIt++; + } + + EXPECT_EQ(Expected, Result); +} From 82ca75fe40f66e515610edb878f45d2d4b0c609a Mon Sep 17 00:00:00 2001 From: "Jiang, Zhiwei" Date: Thu, 19 Jun 2025 15:14:27 +0800 Subject: [PATCH 33/53] Update test Signed-off-by: Jiang, Zhiwei --- clang/unittests/DPCT/ReMigrationTest.cpp | 155 +++++++++++++---------- 1 file changed, 89 insertions(+), 66 deletions(-) diff --git a/clang/unittests/DPCT/ReMigrationTest.cpp b/clang/unittests/DPCT/ReMigrationTest.cpp index 439299a75d4c..d8ce8f9f3da3 100644 --- a/clang/unittests/DPCT/ReMigrationTest.cpp +++ b/clang/unittests/DPCT/ReMigrationTest.cpp @@ -8,46 +8,13 @@ using namespace llvm; using namespace clang::tooling; using namespace clang::dpct; -namespace { - // clang-format off -/* -aaa bb ccc -aaa bb ccc -aaa bb ccc -aaa bb ccc -aaa bb ccc -aaa bb ccc -aaa bb ccc -*/ - // clang-format on -StringRef getLineStringUnittest(clang::tooling::UnifiedPath FilePath, - unsigned LineNumber) { - static std::string S = "aaa bb ccc\n"; - return StringRef(S); -} -unsigned getLineNumberUnittest(clang::tooling::UnifiedPath FilePath, - unsigned Offset) { - static std::vector LineOffsets = {0, 11, 22, 33, 44, 55, 66, 77, 88, 99}; - auto Iter = std::upper_bound(LineOffsets.begin(), LineOffsets.end(), Offset); - if (Iter == LineOffsets.end()) - return LineOffsets.size(); - return std::distance(LineOffsets.begin(), Iter); -} -unsigned getLineBeginOffsetUnittest(clang::tooling::UnifiedPath FilePath, - unsigned LineNumber) { - static std::unordered_map LineOffsets = { - {1, 0}, {2, 11}, {3, 22}, {4, 33}, {5, 44}, {6, 55}, {7, 66}, {8, 77}, {9, 88}, {10, 99}}; - return LineOffsets[LineNumber]; -} -} // namespace - -class ReMigrationTest : public ::testing::Test { +class ReMigrationTest1 : public ::testing::Test { protected: void SetUp() override {} void TearDown() override {} }; -TEST_F(ReMigrationTest, calculateUpdatedRanges) { +TEST_F(ReMigrationTest1, calculateUpdatedRanges) { // clang-format off // Old base: /* @@ -118,7 +85,7 @@ aaa bb ccc } } -TEST_F(ReMigrationTest, groupReplcementsByFile) { +TEST_F(ReMigrationTest1, groupReplcementsByFile) { std::string FilePath1 = clang::tooling::UnifiedPath("file1.cpp").getCanonicalPath().str(); std::string FilePath2 = @@ -171,33 +138,61 @@ TEST_F(ReMigrationTest, groupReplcementsByFile) { } } -TEST_F(ReMigrationTest, splitReplInOrderToNotCrossLines) { +class ReMigrationTest2 : public ::testing::Test { +protected: + void SetUp() override {} + void TearDown() override {} // clang-format off - // Old base: /* -Example: aaabbbbccc dddeeeefff ggghhhhiii zzzzzzzzzz yyyyyyyyyy -// -// Original repl: -// (ccc\ndddeeeefff\nggg) =>(jjj\nkkk) -// (zz\nyy) =>(xx) -// -// Splitted repls: -// (ccc\n) =>(jjj\nkkk) -// (dddeeeefff\n) => "" -// (ggg) => "" -// (zz\n) => (xx) -// (yy) => "" -eee bb ccc */ // clang-format on - getLineStringHook = getLineStringUnittest; - getLineNumberHook = getLineNumberUnittest; - getLineBeginOffsetHook = getLineBeginOffsetUnittest; + static StringRef getLineStringUnittest(clang::tooling::UnifiedPath FilePath, + unsigned LineNumber) { + static std::vector LineString = { + "aaabbbbccc\n", "dddeeeefff\n", "ggghhhhiii\n", "zzzzzzzzzz\n", + "yyyyyyyyyy\n"}; + return StringRef(LineString[LineNumber - 1]); + } + static unsigned getLineNumberUnittest(clang::tooling::UnifiedPath FilePath, + unsigned Offset) { + static std::vector LineOffsets = {0, 11, 22, 33, 44}; + auto Iter = + std::upper_bound(LineOffsets.begin(), LineOffsets.end(), Offset); + if (Iter == LineOffsets.end()) + return LineOffsets.size(); + return std::distance(LineOffsets.begin(), Iter); + } + static unsigned + getLineBeginOffsetUnittest(clang::tooling::UnifiedPath FilePath, + unsigned LineNumber) { + static std::unordered_map LineOffsets = { + {1, 0}, {2, 11}, {3, 22}, {4, 33}, {5, 44}}; + return LineOffsets[LineNumber]; + } +}; + +TEST_F(ReMigrationTest2, splitReplInOrderToNotCrossLines) { + // Example: + // + // Original repl: + // (ccc\ndddeeeefff\nggg) =>(jjj\nkkk) + // (zz\nyy) =>(xx) + // + // Splitted repls: + // (ccc\n) =>(jjj\nkkk) + // (dddeeeefff\n) => "" + // (ggg) => "" + // (zz\n) => (xx) + // (yy) => "" + + getLineStringHook = this->getLineStringUnittest; + getLineNumberHook = this->getLineNumberUnittest; + getLineBeginOffsetHook = this->getLineBeginOffsetUnittest; std::vector Repls = {Replacement("file1.cpp", 7, 18, "jjj\nkkk"), Replacement("file1.cpp", 41, 5, "xx")}; std::vector Expected = { @@ -210,9 +205,11 @@ eee bb ccc EXPECT_EQ(Expected, Result); } -TEST_F(ReMigrationTest, convertReplcementsLineString) { +class ReMigrationTest3 : public ::testing::Test { +protected: + void SetUp() override {} + void TearDown() override {} // clang-format off - // Old base: /* aaa bb ccc aaa bb ccc @@ -225,6 +222,34 @@ aaa bb ccc aaa bb ccc aaa bb ccc */ + // clang-format on + static StringRef getLineStringUnittest(clang::tooling::UnifiedPath FilePath, + unsigned LineNumber) { + static std::string S = "aaa bb ccc\n"; + return StringRef(S); + } + static unsigned getLineNumberUnittest(clang::tooling::UnifiedPath FilePath, + unsigned Offset) { + static std::vector LineOffsets = {0, 11, 22, 33, 44, + 55, 66, 77, 88, 99}; + auto Iter = + std::upper_bound(LineOffsets.begin(), LineOffsets.end(), Offset); + if (Iter == LineOffsets.end()) + return LineOffsets.size(); + return std::distance(LineOffsets.begin(), Iter); + } + static unsigned + getLineBeginOffsetUnittest(clang::tooling::UnifiedPath FilePath, + unsigned LineNumber) { + static std::unordered_map LineOffsets = { + {1, 0}, {2, 11}, {3, 22}, {4, 33}, {5, 44}, + {6, 55}, {7, 66}, {8, 77}, {9, 88}, {10, 99}}; + return LineOffsets[LineNumber]; + } +}; + +TEST_F(ReMigrationTest3, convertReplcementsLineString) { + // clang-format off // After appling repls: /* aaa zzz ccc @@ -239,16 +264,15 @@ aaa bb ddd eee bb ccc */ // clang-format on - getLineStringHook = getLineStringUnittest; - getLineNumberHook = getLineNumberUnittest; - getLineBeginOffsetHook = getLineBeginOffsetUnittest; + getLineStringHook = this->getLineStringUnittest; + getLineNumberHook = this->getLineNumberUnittest; + getLineBeginOffsetHook = this->getLineBeginOffsetUnittest; std::vector Repls = { Replacement("file1.cpp", 4, 2, "zzz"), Replacement("file1.cpp", 64, 3, "q\nqqq"), Replacement("file1.cpp", 33, 11, "aaa yyy ccc\n"), Replacement("file1.cpp", 11, 0, "ppp "), - Replacement("file1.cpp", 84, 18, "ddd\neee") - }; + Replacement("file1.cpp", 84, 18, "ddd\neee")}; std::map Expected = {{1, "aaa zzz ccc\n"}, {2, "ppp aaa bb ccc\n"}, {4, "aaa yyy ccc\n"}, @@ -256,15 +280,14 @@ eee bb ccc {7, "aa bb ccc\n"}, {8, "aaa bb ddd\neee"}, {9, ""}, - {10, " bb ccc\n"} - }; + {10, " bb ccc\n"}}; auto Result = convertReplcementsLineString(Repls); EXPECT_EQ(Expected, Result); } -TEST_F(ReMigrationTest, mergeMapsByLine) { - getLineBeginOffsetHook = getLineBeginOffsetUnittest; - getLineStringHook = getLineStringUnittest; +TEST_F(ReMigrationTest3, mergeMapsByLine) { + getLineBeginOffsetHook = this->getLineBeginOffsetUnittest; + getLineStringHook = this->getLineStringUnittest; std::map MapA = {{1, "zzzzz\n"}, {3, "xxxx1\n"}, {4, "wwww1\n"}, {5, "vvvvv\n"}, {7, "ppppp\n"}, {9, "iiijjjkkk\n"}}; From 882661fb3ef69ffbabf5ebceb285278204379d6a Mon Sep 17 00:00:00 2001 From: "Jiang, Zhiwei" Date: Thu, 19 Jun 2025 16:20:49 +0800 Subject: [PATCH 34/53] Small fix Signed-off-by: Jiang, Zhiwei --- clang/lib/DPCT/IncMigration/ReMigration.cpp | 5 ++- clang/lib/DPCT/IncMigration/ReMigration.h | 5 +++ clang/unittests/DPCT/ReMigrationTest.cpp | 50 +++++++++++++++++++++ 3 files changed, 58 insertions(+), 2 deletions(-) diff --git a/clang/lib/DPCT/IncMigration/ReMigration.cpp b/clang/lib/DPCT/IncMigration/ReMigration.cpp index f16e28d7db0e..c3c2b82e755b 100644 --- a/clang/lib/DPCT/IncMigration/ReMigration.cpp +++ b/clang/lib/DPCT/IncMigration/ReMigration.cpp @@ -365,7 +365,7 @@ mergeMapsByLine(const std::map &MapA, // Repl_A_4: Replacements in moved files. // // Merge process: -// 1. Merge Repl_C1 and Repl_C2 directly, named Repl_C. There is no conlict. +// 1. Merge Repl_C1 and Repl_C2 directly, named Repl_C. (If there is conlict, keep Repl_C2) // Repl_C can be divided in to 2 parts: // Repl_C_x: Replacements which in ranges of Repl_A_3 or delete hunks in // Repl_A_2/Repl_A_4. @@ -383,6 +383,7 @@ reMigrationMerge(const GitDiffChanges &Repl_A, "Repl_C2 should only have ModifiyFileHunks."); std::vector Repl_C; // Merge Repl_C1 and Repl_C2 + // TODO: resolve conflict. Repl_C.insert(Repl_C.end(), Repl_C1.begin(), Repl_C1.end()); for (const auto &Hunk : Repl_C2.ModifyFileHunks) { tooling::Replacement Replacement( @@ -419,7 +420,7 @@ reMigrationMerge(const GitDiffChanges &Repl_A, // Get Repl_C_y std::map Repl_C_y; - for (const auto &Repl : Repl_B) { + for (const auto &Repl : Repl_C) { // The gitdiff changes are line-based while clang replacements are character-based. // So here assume there is no overlap between delete hunks and replacements. const auto &It = DeletedParts.find(Repl.getFilePath().str()); diff --git a/clang/lib/DPCT/IncMigration/ReMigration.h b/clang/lib/DPCT/IncMigration/ReMigration.h index 6f20b0e56035..faea0e746195 100644 --- a/clang/lib/DPCT/IncMigration/ReMigration.h +++ b/clang/lib/DPCT/IncMigration/ReMigration.h @@ -108,6 +108,11 @@ std::vector mergeMapsByLine(const std::map &MapA, const std::map &MapB, const clang::tooling::UnifiedPath &FilePath); +std::map> +reMigrationMerge(const GitDiffChanges &Repl_A, + const std::vector &Repl_B, + const std::vector &Repl_C1, + const GitDiffChanges &Repl_C2); } // namespace dpct } // namespace clang diff --git a/clang/unittests/DPCT/ReMigrationTest.cpp b/clang/unittests/DPCT/ReMigrationTest.cpp index d8ce8f9f3da3..966fdbb6e174 100644 --- a/clang/unittests/DPCT/ReMigrationTest.cpp +++ b/clang/unittests/DPCT/ReMigrationTest.cpp @@ -323,3 +323,53 @@ TEST_F(ReMigrationTest3, mergeMapsByLine) { EXPECT_EQ(Expected, Result); } + +class ReMigrationTest4 : public ::testing::Test { +protected: + void SetUp() override {} + void TearDown() override {} + // clang-format off +/* +aaa bb ccc +aaa bb ccc +aaa bb ccc +aaa bb ccc +aaa bb ccc +aaa bb ccc +aaa bb ccc +aaa bb ccc +aaa bb ccc +aaa bb ccc +*/ + // clang-format on + static StringRef getLineStringUnittest(clang::tooling::UnifiedPath FilePath, + unsigned LineNumber) { + static std::string S = "aaa bb ccc\n"; + return StringRef(S); + } + static unsigned getLineNumberUnittest(clang::tooling::UnifiedPath FilePath, + unsigned Offset) { + static std::vector LineOffsets = {0, 11, 22, 33, 44, + 55, 66, 77, 88, 99}; + auto Iter = + std::upper_bound(LineOffsets.begin(), LineOffsets.end(), Offset); + if (Iter == LineOffsets.end()) + return LineOffsets.size(); + return std::distance(LineOffsets.begin(), Iter); + } + static unsigned + getLineBeginOffsetUnittest(clang::tooling::UnifiedPath FilePath, + unsigned LineNumber) { + static std::unordered_map LineOffsets = { + {1, 0}, {2, 11}, {3, 22}, {4, 33}, {5, 44}, + {6, 55}, {7, 66}, {8, 77}, {9, 88}, {10, 99}}; + return LineOffsets[LineNumber]; + } +}; + +TEST_F(ReMigrationTest4, reMigrationMerge) { + getLineStringHook = this->getLineStringUnittest; + getLineNumberHook = this->getLineNumberUnittest; + getLineBeginOffsetHook = this->getLineBeginOffsetUnittest; + +} From 2873a7318a1b19e78585b06d2e266ca83273a4f9 Mon Sep 17 00:00:00 2001 From: "Jiang, Zhiwei" Date: Fri, 20 Jun 2025 09:53:36 +0800 Subject: [PATCH 35/53] Add test for mergeC1AndC2 Signed-off-by: Jiang, Zhiwei --- clang/lib/DPCT/IncMigration/ReMigration.cpp | 157 ++++++++++++-------- clang/lib/DPCT/IncMigration/ReMigration.h | 4 + clang/unittests/DPCT/ReMigrationTest.cpp | 45 +++++- 3 files changed, 142 insertions(+), 64 deletions(-) diff --git a/clang/lib/DPCT/IncMigration/ReMigration.cpp b/clang/lib/DPCT/IncMigration/ReMigration.cpp index c3c2b82e755b..ed94242bc93e 100644 --- a/clang/lib/DPCT/IncMigration/ReMigration.cpp +++ b/clang/lib/DPCT/IncMigration/ReMigration.cpp @@ -30,14 +30,14 @@ std::optional> getLineBeginOffsetHook = std::nullopt; namespace clang::dpct { +using namespace clang::tooling; static GitDiffChanges UpstreamChanges; static GitDiffChanges UserChanges; AddFileHunk::AddFileHunk(std::string NewFilePath) - : Hunk(AddFile), - NewFilePath(tooling::UnifiedPath(NewFilePath).getCanonicalPath()) {} + : Hunk(AddFile), NewFilePath(UnifiedPath(NewFilePath).getCanonicalPath()) {} DeleteFileHunk::DeleteFileHunk(std::string OldFilePath) : Hunk(DeleteFile), - OldFilePath(tooling::UnifiedPath(OldFilePath).getCanonicalPath()) {} + OldFilePath(UnifiedPath(OldFilePath).getCanonicalPath()) {} GitDiffChanges &getUpstreamChanges() { return UpstreamChanges; } GitDiffChanges &getUserChanges() { return UserChanges; } static void dumpGitDiffChanges(const GitDiffChanges &GHC) { @@ -85,8 +85,7 @@ void tryLoadingUpstreamChangesAndUserChanges() { } } -static StringRef getLineString(clang::tooling::UnifiedPath FilePath, - unsigned LineNumber) { +static StringRef getLineString(UnifiedPath FilePath, unsigned LineNumber) { #ifndef NDEBUG if (getLineStringHook.has_value()) { return getLineStringHook.value()(FilePath, LineNumber); @@ -97,8 +96,7 @@ static StringRef getLineString(clang::tooling::UnifiedPath FilePath, return Line; } -static unsigned getLineNumber(clang::tooling::UnifiedPath FilePath, - unsigned Offset) { +static unsigned getLineNumber(UnifiedPath FilePath, unsigned Offset) { #ifndef NDEBUG if (getLineNumberHook.has_value()) { return getLineNumberHook.value()(FilePath, Offset); @@ -108,8 +106,7 @@ static unsigned getLineNumber(clang::tooling::UnifiedPath FilePath, return FileInfo->getLineNumber(Offset); } -static unsigned getLineBeginOffset(clang::tooling::UnifiedPath FilePath, - unsigned LineNumber) { +static unsigned getLineBeginOffset(UnifiedPath FilePath, unsigned LineNumber) { #ifndef NDEBUG if (getLineBeginOffsetHook.has_value()) { return getLineBeginOffsetHook.value()(FilePath, LineNumber); @@ -138,7 +135,7 @@ calculateUpdatedRanges(const clang::tooling::Replacements &Repls, for (const auto &R : NewRepl) { // Check if the range (BOffset, EOffset - BOffset) is overlapped with any // repl in Repls - std::optional MaxNotGreater = std::nullopt; + std::optional MaxNotGreater = std::nullopt; for (const auto &ExistingR : Repls) { if (ExistingR.getOffset() <= R.getOffset()) MaxNotGreater = ExistingR; @@ -146,7 +143,8 @@ calculateUpdatedRanges(const clang::tooling::Replacements &Repls, break; } if (MaxNotGreater.has_value()) { - if (MaxNotGreater->getOffset() + MaxNotGreater->getLength() > R.getOffset()) + if (MaxNotGreater->getOffset() + MaxNotGreater->getLength() > + R.getOffset()) continue; // has overlap } @@ -155,16 +153,15 @@ calculateUpdatedRanges(const clang::tooling::Replacements &Repls, Repls.getShiftedCodePosition(R.getOffset() + R.getLength()); if (BOffset > EOffset) continue; - llvm::cantFail(Result.add(tooling::Replacement( + llvm::cantFail(Result.add(Replacement( R.getFilePath(), BOffset, EOffset - BOffset, R.getReplacementText()))); } return Result; } -std::map> -groupReplcementsByFile( - const std::vector &Repls) { - std::map> Result; +std::map> +groupReplcementsByFile(const std::vector &Repls) { + std::map> Result; for (const auto &R : Repls) { Result[R.getFilePath().str()].push_back(R); } @@ -184,10 +181,10 @@ groupReplcementsByFile( // (ccc\n) =>(jjj\nkkk) // (dddeeefff\n) => "" // (ggg) => "" -std::vector splitReplInOrderToNotCrossLines( - const std::vector &InRepls) { +std::vector +splitReplInOrderToNotCrossLines(const std::vector &InRepls) { std::string FilePath = InRepls[0].getFilePath().str(); - std::vector Result; + std::vector Result; for (const auto &Repl : InRepls) { unsigned StartOffset = Repl.getOffset(); @@ -231,13 +228,13 @@ std::vector splitReplInOrderToNotCrossLines( } std::map -convertReplcementsLineString(const std::vector &InRepls) { - std::vector Replacements = +convertReplcementsLineString(const std::vector &InRepls) { + std::vector Replacements = splitReplInOrderToNotCrossLines(InRepls); - tooling::UnifiedPath FilePath(InRepls[0].getFilePath()); + UnifiedPath FilePath(InRepls[0].getFilePath()); // group replacement by line - std::map> ReplacementsByLine; + std::map> ReplacementsByLine; for (const auto &Repl : Replacements) { unsigned LineNum = getLineNumber(FilePath, Repl.getOffset()); ReplacementsByLine[LineNum].push_back(Repl); @@ -268,26 +265,26 @@ convertReplcementsLineString(const std::vector &InRepls) { static std::map convertReplcementsLineString(const tooling::Replacements &Repls) { - std::vector ReplsVec; + std::vector ReplsVec; for (const auto &R : Repls) { ReplsVec.push_back(R); } return convertReplcementsLineString(ReplsVec); } -std::vector +std::vector mergeMapsByLine(const std::map &MapA, const std::map &MapB, - const clang::tooling::UnifiedPath &FilePath) { + const UnifiedPath &FilePath) { auto genReplacement = [&](unsigned LineNumber, const std::string &LineContent) { unsigned Offset = getLineBeginOffset(FilePath, LineNumber); unsigned StrLen = getLineString(FilePath, LineNumber).size(); - return tooling::Replacement(FilePath.getCanonicalPath(), Offset, StrLen, - LineContent); + return Replacement(FilePath.getCanonicalPath(), Offset, StrLen, + LineContent); }; - std::vector Result; + std::vector Result; auto ItA = MapA.begin(); auto ItB = MapB.begin(); @@ -337,6 +334,50 @@ mergeMapsByLine(const std::map &MapA, return Result; } +static bool hasConflict(const Replacement &R1, const Replacement &R2) { + if (R1.getFilePath() != R2.getFilePath()) + return false; + if (R1.getOffset() == R2.getOffset()) { + if (R1.getLength() && R2.getLength()) { + return true; + } + } + if ((R1.getOffset() < R2.getOffset() && + R1.getOffset() + R1.getLength() > R2.getOffset()) || + (R2.getOffset() < R1.getOffset() && + R2.getOffset() + R2.getLength() > R1.getOffset())) { + return true; + } + return false; +} + +// Merge Repl_C1 and Repl_C2. If has conflict, keep repl from Repl_C2. +std::vector mergeC1AndC2(const std::vector &Repl_C1, + const GitDiffChanges &Repl_C2) { + std::vector Result; + std::vector Repl_C2_vec; + std::for_each(Repl_C2.ModifyFileHunks.begin(), Repl_C2.ModifyFileHunks.end(), + [&Repl_C2_vec](const Replacement &Hunk) { + Replacement Replacement(Hunk.getFilePath(), Hunk.getOffset(), + Hunk.getLength(), + Hunk.getReplacementText()); + Repl_C2_vec.push_back(Replacement); + }); + for (const auto &ReplInC1 : Repl_C1) { + bool HasConflict = false; + for (const auto &ReplInC2 : Repl_C2_vec) { + if (HasConflict = hasConflict(ReplInC1, ReplInC2)) + break; + } + if (!HasConflict) { + Result.push_back(ReplInC1); + } + } + Result.insert(Result.end(), Repl_C2_vec.begin(), Repl_C2_vec.end()); + return Result; +} + +// clang-format off // Repl A // [CUDA code 1] -----------------------------------------> [CUDA code 2] // | / | @@ -364,8 +405,11 @@ mergeMapsByLine(const std::map &MapA, // Repl_A_3: Deleted files. // Repl_A_4: Replacements in moved files. // +// clang-format on +// // Merge process: -// 1. Merge Repl_C1 and Repl_C2 directly, named Repl_C. (If there is conlict, keep Repl_C2) +// 1. Merge Repl_C1 and Repl_C2 directly, named Repl_C. If there is conlict, +// we keep Repl_C2. // Repl_C can be divided in to 2 parts: // Repl_C_x: Replacements which in ranges of Repl_A_3 or delete hunks in // Repl_A_2/Repl_A_4. @@ -373,24 +417,14 @@ mergeMapsByLine(const std::map &MapA, // Repl_C_x will be ignored during this merge. // 2. Shfit Repl_C_y with Repl_A, called Repl_D. // 3. Merge Repl_D and Repl_B. May have conflicts. -std::map> -reMigrationMerge(const GitDiffChanges &Repl_A, - const std::vector &Repl_B, - const std::vector &Repl_C1, - const GitDiffChanges &Repl_C2) { +std::map> reMigrationMerge( + const GitDiffChanges &Repl_A, const std::vector &Repl_B, + const std::vector &Repl_C1, const GitDiffChanges &Repl_C2) { assert(Repl_C2.AddFileHunks.empty() && Repl_C2.DeleteFileHunks.empty() && Repl_C2.MoveFileHunks.empty() && "Repl_C2 should only have ModifiyFileHunks."); - std::vector Repl_C; - // Merge Repl_C1 and Repl_C2 - // TODO: resolve conflict. - Repl_C.insert(Repl_C.end(), Repl_C1.begin(), Repl_C1.end()); - for (const auto &Hunk : Repl_C2.ModifyFileHunks) { - tooling::Replacement Replacement( - Hunk.getFilePath(), Hunk.getOffset(), Hunk.getLength(), - Hunk.getReplacementText()); - Repl_C.push_back(Replacement); - } + // Merge Repl_C1 and Repl_C2. If has conflict, keep repl from Repl_C2. + std::vector Repl_C = mergeC1AndC2(Repl_C1, Repl_C2); // Convert vector in Repl_A to map for quick lookup. std::map> @@ -402,8 +436,8 @@ reMigrationMerge(const GitDiffChanges &Repl_A, Hunk.getLength(); } llvm::cantFail(ModifiedParts[Hunk.getFilePath().str()].add( - tooling::Replacement(Hunk.getFilePath().str(), Hunk.getOffset(), - Hunk.getLength(), Hunk.getReplacementText()))); + Replacement(Hunk.getFilePath().str(), Hunk.getOffset(), + Hunk.getLength(), Hunk.getReplacementText()))); } for (const auto &Hunk : Repl_A.MoveFileHunks) { if (Hunk.getLength() != 0 && Hunk.getReplacementText().size() == 0) { @@ -411,8 +445,8 @@ reMigrationMerge(const GitDiffChanges &Repl_A, Hunk.getLength(); } llvm::cantFail(ModifiedParts[Hunk.getFilePath().str()].add( - tooling::Replacement(Hunk.getFilePath().str(), Hunk.getOffset(), - Hunk.getLength(), Hunk.getReplacementText()))); + Replacement(Hunk.getFilePath().str(), Hunk.getOffset(), + Hunk.getLength(), Hunk.getReplacementText()))); } for (const auto &Hunk : Repl_A.DeleteFileHunks) { DeletedParts[Hunk.getOldFilePath()] = std::map(); @@ -421,27 +455,26 @@ reMigrationMerge(const GitDiffChanges &Repl_A, // Get Repl_C_y std::map Repl_C_y; for (const auto &Repl : Repl_C) { - // The gitdiff changes are line-based while clang replacements are character-based. - // So here assume there is no overlap between delete hunks and replacements. + // The gitdiff changes are line-based while clang replacements are + // character-based. So here assume there is no overlap (only repl totally + // covered by delete hunk) between delete hunks and replacements. const auto &It = DeletedParts.find(Repl.getFilePath().str()); if (It == DeletedParts.end()) { llvm::cantFail(Repl_C_y[Repl.getFilePath().str()].add( - tooling::Replacement(Repl.getFilePath().str(), Repl.getOffset(), - Repl.getLength(), Repl.getReplacementText()))); + Replacement(Repl.getFilePath().str(), Repl.getOffset(), + Repl.getLength(), Repl.getReplacementText()))); continue; } // Check if the replacement is in a deleted part. - // TODO: Use Interval Tree to speed up the lookup. for (const auto &Part : It->second) { - if (Repl.getOffset() >= Part.first && - Repl.getOffset() + Repl.getLength() <= Part.first + Part.second) { + if (hasConflict(Repl, Replacement(Repl.getFilePath(), Part.first, + Part.second, ""))) break; - } } llvm::cantFail(Repl_C_y[Repl.getFilePath().str()].add( - tooling::Replacement(Repl.getFilePath().str(), Repl.getOffset(), - Repl.getLength(), Repl.getReplacementText()))); + Replacement(Repl.getFilePath().str(), Repl.getOffset(), + Repl.getLength(), Repl.getReplacementText()))); } // Shift Repl_C_y with Repl_A(ModifiedParts) @@ -461,10 +494,8 @@ reMigrationMerge(const GitDiffChanges &Repl_A, // Group Repl_B by file const auto Repl_B_by_file = groupReplcementsByFile(Repl_B); // Merge Repl_D and Repl_B - // 1. we should convert the replacements to a map . We will have 2 maps. - // 2. we need a vector for current file (CUDA code 2) - // 3. merge line by line - std::map> Result; + // Convert the repls to a map then merge line by line + std::map> Result; for (const auto &Pair : Repl_B_by_file) { std::map ReplBInLines = convertReplcementsLineString(Pair.second); diff --git a/clang/lib/DPCT/IncMigration/ReMigration.h b/clang/lib/DPCT/IncMigration/ReMigration.h index faea0e746195..cba40fcbcf8a 100644 --- a/clang/lib/DPCT/IncMigration/ReMigration.h +++ b/clang/lib/DPCT/IncMigration/ReMigration.h @@ -108,6 +108,10 @@ std::vector mergeMapsByLine(const std::map &MapA, const std::map &MapB, const clang::tooling::UnifiedPath &FilePath); +std::vector +mergeC1AndC2(const std::vector &Repl_C1, + const GitDiffChanges &Repl_C2); + std::map> reMigrationMerge(const GitDiffChanges &Repl_A, const std::vector &Repl_B, diff --git a/clang/unittests/DPCT/ReMigrationTest.cpp b/clang/unittests/DPCT/ReMigrationTest.cpp index 966fdbb6e174..fcbb940ed614 100644 --- a/clang/unittests/DPCT/ReMigrationTest.cpp +++ b/clang/unittests/DPCT/ReMigrationTest.cpp @@ -324,6 +324,49 @@ TEST_F(ReMigrationTest3, mergeMapsByLine) { EXPECT_EQ(Expected, Result); } +TEST_F(ReMigrationTest1, mergeC1AndC2) { + std::vector Repl_C1 = { + Replacement("file1.cpp", 10, 0, "aaa"), + Replacement("file1.cpp", 11, 1, "bbb"), + Replacement("file1.cpp", 20, 20, "ccc"), + Replacement("file2.cpp", 20, 20, "zzz"), + Replacement("file2.cpp", 40, 1, "yyy"), + }; + GitDiffChanges Repl_C2; + Repl_C2.ModifyFileHunks = { + Replacement("file1.cpp", 10, 0, "ddd"), + Replacement("file1.cpp", 10, 2, "eee"), + Replacement("file1.cpp", 40, 2, "fff"), + Replacement("file2.cpp", 21, 3, "xxx"), + Replacement("file2.cpp", 41, 1, "www"), + }; + + std::vector Result = mergeC1AndC2(Repl_C1, Repl_C2); + std::vector Expected = {Replacement("file1.cpp", 10, 0, "aaa"), + Replacement("file1.cpp", 10, 0, "ddd"), + Replacement("file1.cpp", 10, 2, "eee"), + Replacement("file1.cpp", 20, 20, "ccc"), + Replacement("file1.cpp", 40, 2, "fff"), + Replacement("file2.cpp", 21, 3, "xxx"), + Replacement("file2.cpp", 40, 1, "yyy"), + Replacement("file2.cpp", 41, 1, "www")}; + std::sort(Result.begin(), Result.end()); + std::sort(Expected.begin(), Expected.end()); + + ASSERT_EQ(Expected.size(), Result.size()); + size_t Num = Expected.size(); + auto ExpectedIt = Expected.begin(); + auto ResultIt = Result.begin(); + for (size_t i = 0; i < Num; ++i) { + EXPECT_EQ(ExpectedIt->getFilePath(), ResultIt->getFilePath()); + EXPECT_EQ(ExpectedIt->getOffset(), ResultIt->getOffset()); + EXPECT_EQ(ExpectedIt->getLength(), ResultIt->getLength()); + EXPECT_EQ(ExpectedIt->getReplacementText(), ResultIt->getReplacementText()); + ExpectedIt++; + ResultIt++; + } +} + class ReMigrationTest4 : public ::testing::Test { protected: void SetUp() override {} @@ -371,5 +414,5 @@ TEST_F(ReMigrationTest4, reMigrationMerge) { getLineStringHook = this->getLineStringUnittest; getLineNumberHook = this->getLineNumberUnittest; getLineBeginOffsetHook = this->getLineBeginOffsetUnittest; - + ASSERT_EQ(true, true); // Placeholder for actual test logic } From 50bca902a85b71d105253465c4442ea59015866c Mon Sep 17 00:00:00 2001 From: "Jiang, Zhiwei" Date: Fri, 20 Jun 2025 14:08:25 +0800 Subject: [PATCH 36/53] Update Signed-off-by: Jiang, Zhiwei --- clang/lib/DPCT/IncMigration/ReMigration.cpp | 3 + clang/unittests/DPCT/ReMigrationTest.cpp | 144 ++++++++++++++++---- 2 files changed, 123 insertions(+), 24 deletions(-) diff --git a/clang/lib/DPCT/IncMigration/ReMigration.cpp b/clang/lib/DPCT/IncMigration/ReMigration.cpp index ed94242bc93e..7f5f14433e0a 100644 --- a/clang/lib/DPCT/IncMigration/ReMigration.cpp +++ b/clang/lib/DPCT/IncMigration/ReMigration.cpp @@ -424,6 +424,9 @@ std::map> reMigrationMerge( Repl_C2.MoveFileHunks.empty() && "Repl_C2 should only have ModifiyFileHunks."); // Merge Repl_C1 and Repl_C2. If has conflict, keep repl from Repl_C2. + // TODO: Repl_C1 has name like file1.cpp, file2.cpp, file3.cu, file4.cuh + // but Repl_C2 has name like file1.cpp, file2.cpp.dp.cpp, file3.dp.cpp, file4.dp.hpp + // we need map different file names (or just convert the filename in Repl_C2 to CUDA style) std::vector Repl_C = mergeC1AndC2(Repl_C1, Repl_C2); // Convert vector in Repl_A to map for quick lookup. diff --git a/clang/unittests/DPCT/ReMigrationTest.cpp b/clang/unittests/DPCT/ReMigrationTest.cpp index fcbb940ed614..3da04f125778 100644 --- a/clang/unittests/DPCT/ReMigrationTest.cpp +++ b/clang/unittests/DPCT/ReMigrationTest.cpp @@ -369,31 +369,54 @@ TEST_F(ReMigrationTest1, mergeC1AndC2) { class ReMigrationTest4 : public ::testing::Test { protected: - void SetUp() override {} - void TearDown() override {} - // clang-format off -/* -aaa bb ccc -aaa bb ccc -aaa bb ccc -aaa bb ccc -aaa bb ccc -aaa bb ccc -aaa bb ccc -aaa bb ccc -aaa bb ccc -aaa bb ccc -*/ - // clang-format on + inline static std::vector CUDACodeV1 = {}; + inline static std::vector LineOffsets = {}; + void SetUp() override { + // clang-format off + const std::string CUDACode = R"(#include + +#define CUDA_CHECK(call) \ + do { \ + cudaError_t err = call; \ + if (err != cudaSuccess) { \ + printf("CUDA error in %s at line %d: %s\n", __FILE__, __LINE__, \ + cudaGetErrorString(err)); \ + exit(EXIT_FAILURE); \ + } \ + } while (0) + +void foo() { + float *f; + CUDA_CHECK(cudaMalloc(&f, 100 * sizeof(float))); + float *g; + CUDA_CHECK(cudaMalloc(&g, 100 * sizeof(float))); + cudaMemcpy(f, g, 100 * sizeof(float), cudaMemcpyDeviceToDevice); + cudaDeviceSynchronize(); + cudaFree(f); + cudaFree(g); +} +)"; + // clang-format on + std::istringstream ISS(CUDACode); + std::string Line; + while (std::getline(ISS, Line)) { + if (ISS.eof() && Line.empty()) + break; + Line += '\n'; + LineOffsets.push_back(LineOffsets.empty() ? 0 + : LineOffsets.back() + + CUDACodeV1.back().size()); + CUDACodeV1.push_back(Line); + } + LineOffsets.insert(LineOffsets.begin(), 0); + } + void TearDown() override { CUDACodeV1.clear(); } static StringRef getLineStringUnittest(clang::tooling::UnifiedPath FilePath, unsigned LineNumber) { - static std::string S = "aaa bb ccc\n"; - return StringRef(S); + return StringRef(CUDACodeV1[LineNumber - 1]); } static unsigned getLineNumberUnittest(clang::tooling::UnifiedPath FilePath, unsigned Offset) { - static std::vector LineOffsets = {0, 11, 22, 33, 44, - 55, 66, 77, 88, 99}; auto Iter = std::upper_bound(LineOffsets.begin(), LineOffsets.end(), Offset); if (Iter == LineOffsets.end()) @@ -403,9 +426,6 @@ aaa bb ccc static unsigned getLineBeginOffsetUnittest(clang::tooling::UnifiedPath FilePath, unsigned LineNumber) { - static std::unordered_map LineOffsets = { - {1, 0}, {2, 11}, {3, 22}, {4, 33}, {5, 44}, - {6, 55}, {7, 66}, {8, 77}, {9, 88}, {10, 99}}; return LineOffsets[LineNumber]; } }; @@ -414,5 +434,81 @@ TEST_F(ReMigrationTest4, reMigrationMerge) { getLineStringHook = this->getLineStringUnittest; getLineNumberHook = this->getLineNumberUnittest; getLineBeginOffsetHook = this->getLineBeginOffsetUnittest; - ASSERT_EQ(true, true); // Placeholder for actual test logic + + std::vector Repl_C1 = { + Replacement("test.cu", 0, 0, + "#include \n#include \n"), + Replacement( + "test.cu", 20, 0, + "/*\nDPCT1009:0: SYCL reports errors using exceptions and does not " + "use error codes. Please replace the \"get_error_string_dummy(...)\" " + "with a real error-handling function.\n*/\n"), + Replacement("test.cu", 186, 11, "dpct::err0"), + Replacement("test.cu", 267, 325, ""), + Replacement("test.cu", 694, 0, " try "), + Replacement( + "test.cu", 695, 0, + "\n dpct::device_ext &dev_ct1 = dpct::get_current_device();\n " + "sycl::queue &q_ct1 = dev_ct1.in_order_queue();"), + Replacement( + "test.cu", 721, 35, + "DPCT_CHECK_ERROR(f = sycl::malloc_device(100, q_ct1))"), + Replacement( + "test.cu", 784, 35, + "DPCT_CHECK_ERROR(g = sycl::malloc_device(100, q_ct1))"), + Replacement("test.cu", 824, 63, + "q_ct1.memcpy(f, g, 100 * sizeof(float))"), + Replacement("test.cu", 891, 23, "dev_ct1.queues_wait_and_throw()"), + Replacement("test.cu", 918, 11, "dpct::dpct_free(f, q_ct1)"), + Replacement("test.cu", 933, 11, "dpct::dpct_free(g, q_ct1)"), + Replacement("test.cu", 947, 0, + "\ncatch (sycl::exception const &exc) {\n std::cerr << " + "exc.what() << \"Exception caught at file:\" << __FILE__ << " + "\", line:\" << __LINE__ << std::endl;\n std::exit(1);\n}")}; + + std::vector Repl_C2 = { + Replacement("test.dp.cpp", 70, 527, "void foo() {\n"), + Replacement("test.dp.cpp", 716, 76, + " f = sycl::malloc_device(100, q_ct1);\n"), + Replacement("test.dp.cpp", 804, 76, + " g = sycl::malloc_device(100, q_ct1);\n")}; + + std::vector Repl_A = { + Replacement("test.cu", 696, 63, ""), + Replacement( + "test.cu", 822, 67, + " float *h;\n CUDA_CHECK(cudaMalloc(&h, 100 * sizeof(float)));\n"), + Replacement("test.cu", 916, 15, ""), + Replacement("test.cu", 946, 0, " cudaFree(h);\n")}; + + std::vector Repl_B = { + Replacement("test.cu", 0, 0, + "#include \n#include \n"), + Replacement( + "test.cu", 20, 0, + "/*\nDPCT1009:0: SYCL reports errors using exceptions and does not " + "use error codes. Please replace the \"get_error_string_dummy(...)\" " + "with a real error-handling function.\n*/\n"), + Replacement("test.cu", 186, 11, "dpct::err0"), + Replacement("test.cu", 267, 325, ""), + Replacement("test.cu", 694, 0, " try "), + Replacement( + "test.cu", 695, 0, + "\n dpct::device_ext &dev_ct1 = dpct::get_current_device();\n " + "sycl::queue &q_ct1 = dev_ct1.in_order_queue();"), + Replacement( + "test.cu", 721, 35, + "DPCT_CHECK_ERROR(g = sycl::malloc_device(100, q_ct1))"), + Replacement( + "test.cu", 784, 35, + "DPCT_CHECK_ERROR(h = sycl::malloc_device(100, q_ct1))"), + Replacement("test.cu", 824, 23, "dev_ct1.queues_wait_and_throw()"), + Replacement("test.cu", 851, 11, "dpct::dpct_free(g, q_ct1)"), + Replacement("test.cu", 866, 11, "dpct::dpct_free(h, q_ct1)"), + Replacement("test.cu", 880, 0, + "\ncatch (sycl::exception const &exc) {\n std::cerr << " + "exc.what() << \"Exception caught at file:\" << __FILE__ << " + "\", line:\" << __LINE__ << std::endl;\n std::exit(1);\n}")}; + + ASSERT_EQ(true, false); // Placeholder for actual test logic } From c700eceb4ddddaa3f949181413b3982b6cff78ac Mon Sep 17 00:00:00 2001 From: "Jiang, Zhiwei" Date: Fri, 20 Jun 2025 15:26:22 +0800 Subject: [PATCH 37/53] Update Signed-off-by: Jiang, Zhiwei --- clang/lib/DPCT/IncMigration/ReMigration.cpp | 33 +++++++------ clang/lib/DPCT/IncMigration/ReMigration.h | 15 ++++-- clang/unittests/DPCT/ReMigrationTest.cpp | 55 ++++++++++++++------- 3 files changed, 65 insertions(+), 38 deletions(-) diff --git a/clang/lib/DPCT/IncMigration/ReMigration.cpp b/clang/lib/DPCT/IncMigration/ReMigration.cpp index 7f5f14433e0a..149a7f83213c 100644 --- a/clang/lib/DPCT/IncMigration/ReMigration.cpp +++ b/clang/lib/DPCT/IncMigration/ReMigration.cpp @@ -255,8 +255,6 @@ convertReplcementsLineString(const std::vector &InRepls) { NewLineStr += Repl.getReplacementText().str(); Pos = StrOffset + Repl.getLength(); } - std::cout << "OriginalLineStr:" << OriginalLineStr << "!!!" << std::endl; - std::cout << "Pos:" << Pos << std::endl; NewLineStr += OriginalLineStr.substr(Pos); Result[LineNum] = NewLineStr; } @@ -352,17 +350,22 @@ static bool hasConflict(const Replacement &R1, const Replacement &R2) { } // Merge Repl_C1 and Repl_C2. If has conflict, keep repl from Repl_C2. -std::vector mergeC1AndC2(const std::vector &Repl_C1, - const GitDiffChanges &Repl_C2) { +std::vector mergeC1AndC2( + const std::vector &Repl_C1, const GitDiffChanges &Repl_C2, + const std::map + &FileNameMap) { std::vector Result; std::vector Repl_C2_vec; - std::for_each(Repl_C2.ModifyFileHunks.begin(), Repl_C2.ModifyFileHunks.end(), - [&Repl_C2_vec](const Replacement &Hunk) { - Replacement Replacement(Hunk.getFilePath(), Hunk.getOffset(), - Hunk.getLength(), - Hunk.getReplacementText()); - Repl_C2_vec.push_back(Replacement); - }); + std::for_each( + Repl_C2.ModifyFileHunks.begin(), Repl_C2.ModifyFileHunks.end(), + [&Repl_C2_vec, FileNameMap](const Replacement &Hunk) { + UnifiedPath OldFilePath = + FileNameMap.at(UnifiedPath(Hunk.getFilePath())); + Replacement Replacement(OldFilePath.getCanonicalPath(), + Hunk.getOffset(), Hunk.getLength(), + Hunk.getReplacementText()); + Repl_C2_vec.push_back(Replacement); + }); for (const auto &ReplInC1 : Repl_C1) { bool HasConflict = false; for (const auto &ReplInC2 : Repl_C2_vec) { @@ -419,15 +422,17 @@ std::vector mergeC1AndC2(const std::vector &Repl_C1, // 3. Merge Repl_D and Repl_B. May have conflicts. std::map> reMigrationMerge( const GitDiffChanges &Repl_A, const std::vector &Repl_B, - const std::vector &Repl_C1, const GitDiffChanges &Repl_C2) { + const std::vector &Repl_C1, const GitDiffChanges &Repl_C2, + const std::map + &FileNameMap) { assert(Repl_C2.AddFileHunks.empty() && Repl_C2.DeleteFileHunks.empty() && Repl_C2.MoveFileHunks.empty() && "Repl_C2 should only have ModifiyFileHunks."); // Merge Repl_C1 and Repl_C2. If has conflict, keep repl from Repl_C2. // TODO: Repl_C1 has name like file1.cpp, file2.cpp, file3.cu, file4.cuh // but Repl_C2 has name like file1.cpp, file2.cpp.dp.cpp, file3.dp.cpp, file4.dp.hpp - // we need map different file names (or just convert the filename in Repl_C2 to CUDA style) - std::vector Repl_C = mergeC1AndC2(Repl_C1, Repl_C2); + // we need convert the filename in Repl_C2 to CUDA style + std::vector Repl_C = mergeC1AndC2(Repl_C1, Repl_C2, FileNameMap); // Convert vector in Repl_A to map for quick lookup. std::map> diff --git a/clang/lib/DPCT/IncMigration/ReMigration.h b/clang/lib/DPCT/IncMigration/ReMigration.h index cba40fcbcf8a..e76a2fd21ee2 100644 --- a/clang/lib/DPCT/IncMigration/ReMigration.h +++ b/clang/lib/DPCT/IncMigration/ReMigration.h @@ -110,13 +110,18 @@ mergeMapsByLine(const std::map &MapA, const clang::tooling::UnifiedPath &FilePath); std::vector mergeC1AndC2(const std::vector &Repl_C1, - const GitDiffChanges &Repl_C2); + const GitDiffChanges &Repl_C2, + const std::map &FileNameMap); std::map> -reMigrationMerge(const GitDiffChanges &Repl_A, - const std::vector &Repl_B, - const std::vector &Repl_C1, - const GitDiffChanges &Repl_C2); +reMigrationMerge( + const GitDiffChanges &Repl_A, + const std::vector &Repl_B, + const std::vector &Repl_C1, + const GitDiffChanges &Repl_C2, + const std::map &FileNameMap); } // namespace dpct } // namespace clang diff --git a/clang/unittests/DPCT/ReMigrationTest.cpp b/clang/unittests/DPCT/ReMigrationTest.cpp index 3da04f125778..cf59301d3ac2 100644 --- a/clang/unittests/DPCT/ReMigrationTest.cpp +++ b/clang/unittests/DPCT/ReMigrationTest.cpp @@ -326,30 +326,45 @@ TEST_F(ReMigrationTest3, mergeMapsByLine) { TEST_F(ReMigrationTest1, mergeC1AndC2) { std::vector Repl_C1 = { - Replacement("file1.cpp", 10, 0, "aaa"), - Replacement("file1.cpp", 11, 1, "bbb"), - Replacement("file1.cpp", 20, 20, "ccc"), - Replacement("file2.cpp", 20, 20, "zzz"), - Replacement("file2.cpp", 40, 1, "yyy"), + Replacement("file1.cu", 10, 0, "aaa"), + Replacement("file1.cu", 11, 1, "bbb"), + Replacement("file1.cu", 20, 20, "ccc"), + Replacement("file2.cu", 20, 20, "zzz"), + Replacement("file2.cu", 40, 1, "yyy"), + Replacement("file3.cpp", 0, 1, "a"), + Replacement("file4.cpp", 2, 1, "b"), }; GitDiffChanges Repl_C2; Repl_C2.ModifyFileHunks = { - Replacement("file1.cpp", 10, 0, "ddd"), - Replacement("file1.cpp", 10, 2, "eee"), - Replacement("file1.cpp", 40, 2, "fff"), - Replacement("file2.cpp", 21, 3, "xxx"), - Replacement("file2.cpp", 41, 1, "www"), + Replacement("file1.dp.cpp", 10, 0, "ddd"), + Replacement("file1.dp.cpp", 10, 2, "eee"), + Replacement("file1.dp.cpp", 40, 2, "fff"), + Replacement("file2.dp.cpp", 21, 3, "xxx"), + Replacement("file2.dp.cpp", 41, 1, "www"), + Replacement("file3.cpp.dp.cpp", 10, 1, "a"), + Replacement("file4.cpp", 12, 1, "b"), }; + const std::map FileNameMap = { + {"file1.dp.cpp", "file1.cu"}, + {"file2.dp.cpp", "file2.cu"}, + {"file3.cpp.dp.cpp", "file3.cpp"}, + {"file4.cpp", "file4.cpp"}}; - std::vector Result = mergeC1AndC2(Repl_C1, Repl_C2); - std::vector Expected = {Replacement("file1.cpp", 10, 0, "aaa"), - Replacement("file1.cpp", 10, 0, "ddd"), - Replacement("file1.cpp", 10, 2, "eee"), - Replacement("file1.cpp", 20, 20, "ccc"), - Replacement("file1.cpp", 40, 2, "fff"), - Replacement("file2.cpp", 21, 3, "xxx"), - Replacement("file2.cpp", 40, 1, "yyy"), - Replacement("file2.cpp", 41, 1, "www")}; + std::vector Result = mergeC1AndC2(Repl_C1, Repl_C2, FileNameMap); + std::vector Expected = { + Replacement("file1.cu", 10, 0, "aaa"), + Replacement("file1.cu", 10, 0, "ddd"), + Replacement("file1.cu", 10, 2, "eee"), + Replacement("file1.cu", 20, 20, "ccc"), + Replacement("file1.cu", 40, 2, "fff"), + Replacement("file2.cu", 21, 3, "xxx"), + Replacement("file2.cu", 40, 1, "yyy"), + Replacement("file2.cu", 41, 1, "www"), + Replacement("file3.cpp", 0, 1, "a"), + Replacement("file4.cpp", 2, 1, "b"), + Replacement("file3.cpp", 10, 1, "a"), + Replacement("file4.cpp", 12, 1, "b"), + }; std::sort(Result.begin(), Result.end()); std::sort(Expected.begin(), Expected.end()); @@ -367,6 +382,7 @@ TEST_F(ReMigrationTest1, mergeC1AndC2) { } } +#if 0 class ReMigrationTest4 : public ::testing::Test { protected: inline static std::vector CUDACodeV1 = {}; @@ -512,3 +528,4 @@ TEST_F(ReMigrationTest4, reMigrationMerge) { ASSERT_EQ(true, false); // Placeholder for actual test logic } +#endif From bf2dd5573e714d6b06248f17d77d03ec68bc6ba0 Mon Sep 17 00:00:00 2001 From: "Jiang, Zhiwei" Date: Thu, 26 Jun 2025 10:55:09 +0800 Subject: [PATCH 38/53] All tests passed Signed-off-by: Jiang, Zhiwei --- clang/lib/DPCT/IncMigration/ReMigration.cpp | 303 +++++++++++++++--- .../tools/dpct/gitdiff2yaml/gitdiff2yaml.cpp | 5 +- clang/unittests/DPCT/ReMigrationTest.cpp | 226 +++++++++---- 3 files changed, 414 insertions(+), 120 deletions(-) diff --git a/clang/lib/DPCT/IncMigration/ReMigration.cpp b/clang/lib/DPCT/IncMigration/ReMigration.cpp index 149a7f83213c..04bfe7200b95 100644 --- a/clang/lib/DPCT/IncMigration/ReMigration.cpp +++ b/clang/lib/DPCT/IncMigration/ReMigration.cpp @@ -5,6 +5,26 @@ // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// +// Workflow: +// CUDA code v1 +// | +// | dpct (--format-range=off) => MainSourceFiles.yaml and *.h.yaml +// v +// SYCL code v1 +// | 1. git init and commit +// | 2. manual format with clang-format +// | 3. manual fix +// v 4. gitdiff2yaml => UserChange.yaml +// SYCL code v1.1 +// +// CUDA code v2 +// | +// | dpct (--format-range=off) +// | + MainSourceFiles.yaml and *.h.yaml +// | + UserChange.yaml +// v +// SYCL code v2 +//===----------------------------------------------------------------------===// #include "ReMigration.h" #include "AnalysisInfo.h" @@ -85,6 +105,8 @@ void tryLoadingUpstreamChangesAndUserChanges() { } } +/// Below 3 functions only used for merging Repl_B and repl_D. So they only +/// need to read CUDA code v2. static StringRef getLineString(UnifiedPath FilePath, unsigned LineNumber) { #ifndef NDEBUG if (getLineStringHook.has_value()) { @@ -121,7 +143,7 @@ static unsigned getLineBeginOffset(UnifiedPath FilePath, unsigned LineNumber) { /// This shfit may have conflicts. /// Since \p NewRepl (from Repl_C_y) is line based and \p Repls (from Repl_A) is /// character based, we assume that if there is a conflict, the range from -/// Repl_C_y is always covering the range from Repl_A. Then we just ignore the +/// Repl_A is always covering the range from Repl_C_y. Then we just ignore the /// \p NewRepl (from Repl_C_y) since the old CUDA code is changed, so the /// migration repl is out-of-date. /// \param Repls Replacements to apply. @@ -169,18 +191,22 @@ groupReplcementsByFile(const std::vector &Repls) { } // If repl range is cross lines, we treat the \n itself belongs to current line. -// Example: -// aaabbbccc -// dddeeefff -// ggghhhiii -// -// Original repl: -// (ccc\ndddeeefff\nggg) =>(jjj\nkkk) +// case: +/* +aaaaa +bbbbb +ccccc +ddddd +*/ +// replacement is: offset:6, length:11, text:xxx\nyyy // -// Splitted repls: -// (ccc\n) =>(jjj\nkkk) -// (dddeeefff\n) => "" -// (ggg) => "" +// If the splited repl of line 2 does not end with \n, if there is conflict at +// this line, the mark will not correct. +// In other words, if there is no conflict, the repl is correct since there is +// \n after the repl. While the conflict mark assumes that the repl is endding +// with \n so it can at the beginning of each line, this is the problem. +// So we need to merge the left part of the last line into the repl (the first +// line). std::vector splitReplInOrderToNotCrossLines(const std::vector &InRepls) { std::string FilePath = InRepls[0].getFilePath().str(); @@ -202,26 +228,30 @@ splitReplInOrderToNotCrossLines(const std::vector &InRepls) { unsigned CurrentOffset = StartOffset; // The first line + // We need merge the left part of the last line into the replacement unsigned LineEndOffset = getLineBeginOffset(FilePath, StartLine + 1); unsigned FirstLineLength = LineEndOffset - StartOffset; - Result.emplace_back(FilePath, CurrentOffset, FirstLineLength, - Repl.getReplacementText()); + Replacement ReplFisrtLine(FilePath, CurrentOffset, FirstLineLength, + Repl.getReplacementText()); CurrentOffset += FirstLineLength; // middle lines for (unsigned Line = StartLine + 1; Line < EndLine; ++Line) { LineEndOffset = getLineBeginOffset(FilePath, Line + 1); - unsigned lineLength = LineEndOffset - CurrentOffset; - Result.emplace_back(Repl.getFilePath(), CurrentOffset, lineLength, ""); - CurrentOffset += lineLength; + unsigned LineLength = LineEndOffset - CurrentOffset; + Result.emplace_back(Repl.getFilePath(), CurrentOffset, LineLength, ""); + CurrentOffset += LineLength; } // The last line + std::string LastLineStr = getLineString(FilePath, EndLine).str(); unsigned LastLineLength = EndOffset - CurrentOffset; - if (LastLineLength > 0) { - Result.emplace_back(Repl.getFilePath(), CurrentOffset, LastLineLength, - ""); - } + Result.emplace_back(Repl.getFilePath(), ReplFisrtLine.getOffset(), + ReplFisrtLine.getLength(), + ReplFisrtLine.getReplacementText().str() + + LastLineStr.substr(LastLineLength)); + Result.emplace_back(Repl.getFilePath(), CurrentOffset, LastLineStr.size(), + ""); } return Result; @@ -300,6 +330,14 @@ mergeMapsByLine(const std::map &MapA, Result.push_back(genReplacement(ItB->first, ItB->second)); ++ItB; } else { + // ItB->first == ItA->first + if (ItB->second == ItA->second) { + // No conflict, just add one of them + Result.push_back(genReplacement(ItA->first, ItA->second)); + ++ItA; + ++ItB; + continue; + } // Conflict line(s) std::vector ConflictA; std::vector ConflictB; @@ -307,13 +345,15 @@ mergeMapsByLine(const std::map &MapA, unsigned ConflictLength = 0; // Collect continuous conflicting lines + unsigned CurrentLineNum = ItA->first; while (ItA != MapA.end() && ItB != MapB.end() && - ItA->first == ItB->first) { + CurrentLineNum == ItA->first && CurrentLineNum == ItB->first) { ConflictA.push_back(ItA->second); ConflictB.push_back(ItB->second); + ConflictLength += getLineString(FilePath, ItA->first).size(); ++ItA; ++ItB; - ConflictLength += getLineString(FilePath, ItA->first).size(); + ++CurrentLineNum; } // generate merged string @@ -349,6 +389,159 @@ static bool hasConflict(const Replacement &R1, const Replacement &R2) { return false; } +class ReplacementMerger { +public: + static std::vector merge(const std::vector &A, + const std::vector &B) { + // 1. 建立A组替换后的offset映射 + auto [a_map, a_modified] = buildAMapping(A); + std::string FilePath = A[0].getFilePath().str(); + + std::vector final_repls; + std::vector a_repls; + for (const auto &repl : a_modified) { + a_repls.push_back( + {FilePath, std::get<0>(repl), std::get<1>(repl), std::get<2>(repl)}); + } + std::vector b_repls = B; + std::sort( + a_repls.begin(), + a_repls.end()); // The length in replA is based on original text, so for + // below calculation which is based on modified text, we + // need to use text.size() instead. + std::sort(b_repls.begin(), b_repls.end()); + + auto IterA = a_repls.begin(); + auto IterB = b_repls.begin(); + + std::optional> + UnfinishedRepl = std::nullopt; + while (IterA != a_repls.end() || IterB != b_repls.end()) { + if (UnfinishedRepl) { + unsigned Off = std::get<0>(*UnfinishedRepl); + unsigned Len = std::get<1>(*UnfinishedRepl); + std::string Text = std::get<2>(*UnfinishedRepl); + if ((Off + Len) < IterA->getOffset() && + (Off + Len) < IterB->getOffset()) { + // Finished + final_repls.push_back( + {FilePath, mapToOriginal(Off, a_map), + mapToOriginal(Off + Len, a_map) - mapToOriginal(Off, a_map), + Text}); + UnfinishedRepl = std::nullopt; + continue; + } + if ((Off + Len) >= IterB->getOffset()) { + // Unfinished repl overlapping with next B repl. + // We process B first because it has higher priority. + std::get<1>(*UnfinishedRepl) = + IterB->getLength() + IterB->getOffset() - Off; + std::get<2>(*UnfinishedRepl) = + Text.substr(0, IterB->getOffset() - Off) + + IterB->getReplacementText().str(); + ++IterB; + continue; + } + // (Off + Len) >= IterA->getOffset() + if ((Off + Len) >= + (IterA->getOffset() + IterA->getReplacementText().size())) { + // case 1: totally cover IterA + ++IterA; + continue; + } + // case 2: partially cover IterA + std::get<1>(*UnfinishedRepl) = + IterA->getReplacementText().size() + IterA->getOffset() - Off; + std::get<2>(*UnfinishedRepl) = + Text + IterA->getReplacementText().str().substr(Off + Len - + IterA->getOffset()); + ++IterA; + continue; + } + if (IterA == a_repls.end()) { + // only left B group replacements + final_repls.push_back( + {FilePath, mapToOriginal(IterB->getOffset(), a_map), + mapToOriginal(IterB->getOffset() + IterB->getLength(), a_map) - + mapToOriginal(IterB->getOffset(), a_map), + IterB->getReplacementText()}); + ++IterB; + continue; + } + if (IterB == b_repls.end()) { + // only left A group replacements + final_repls.push_back(*IterA); + ++IterA; + continue; + } + if (IterA->getOffset() < IterB->getOffset()) { + UnfinishedRepl = std::make_tuple(IterA->getOffset(), + IterA->getReplacementText().size(), + IterA->getReplacementText().str()); + ++IterA; + } else { + UnfinishedRepl = std::make_tuple(IterB->getOffset(), IterB->getLength(), + IterB->getReplacementText().str()); + ++IterB; + } + } + + std::sort(final_repls.begin(), final_repls.end()); + return final_repls; + } + +private: + // 建立A组替换映射关系 + static std::pair>, + std::vector>> + buildAMapping(const std::vector &A) { + std::vector> offset_map; + std::vector> modified_ranges; + unsigned orig_pos = 0; + unsigned modified_pos = 0; + + auto sorted_A = A; + std::sort(sorted_A.begin(), sorted_A.end()); + + for (const auto &repl : sorted_A) { + // 添加替换前的区间 + if (repl.getOffset() > orig_pos) { + unsigned len = repl.getOffset() - orig_pos; + offset_map.emplace_back(modified_pos, orig_pos); + modified_pos += len; + orig_pos += len; + } + + // 记录修改区域 + modified_ranges.emplace_back(modified_pos, repl.getLength(), + repl.getReplacementText().str()); + + // 添加替换后的映射 + offset_map.emplace_back(modified_pos, repl.getOffset()); + modified_pos += repl.getReplacementText().size(); + orig_pos += repl.getLength(); + } + + // 添加最后一段 + offset_map.emplace_back(modified_pos, orig_pos); + return {offset_map, modified_ranges}; + } + + // 将修改后offset映射回原始offset + static unsigned + mapToOriginal(unsigned modified_offset, + const std::vector> &mapping) { + auto it = std::upper_bound( + mapping.begin(), mapping.end(), std::make_pair(modified_offset, 0), + [](const auto &a, const auto &b) { return a.first < b.first; }); + + if (it != mapping.begin()) + --it; + return it->second + (modified_offset - it->first); + } +}; + // Merge Repl_C1 and Repl_C2. If has conflict, keep repl from Repl_C2. std::vector mergeC1AndC2( const std::vector &Repl_C1, const GitDiffChanges &Repl_C2, @@ -356,27 +549,31 @@ std::vector mergeC1AndC2( &FileNameMap) { std::vector Result; std::vector Repl_C2_vec; - std::for_each( - Repl_C2.ModifyFileHunks.begin(), Repl_C2.ModifyFileHunks.end(), - [&Repl_C2_vec, FileNameMap](const Replacement &Hunk) { - UnifiedPath OldFilePath = - FileNameMap.at(UnifiedPath(Hunk.getFilePath())); - Replacement Replacement(OldFilePath.getCanonicalPath(), - Hunk.getOffset(), Hunk.getLength(), - Hunk.getReplacementText()); - Repl_C2_vec.push_back(Replacement); - }); - for (const auto &ReplInC1 : Repl_C1) { - bool HasConflict = false; - for (const auto &ReplInC2 : Repl_C2_vec) { - if (HasConflict = hasConflict(ReplInC1, ReplInC2)) - break; - } - if (!HasConflict) { - Result.push_back(ReplInC1); + std::for_each(Repl_C2.ModifyFileHunks.begin(), Repl_C2.ModifyFileHunks.end(), + [&Repl_C2_vec, FileNameMap](const Replacement &Hunk) { + UnifiedPath OldFilePath = + FileNameMap.at(UnifiedPath(Hunk.getFilePath())); + Replacement Replacement(OldFilePath.getCanonicalPath(), + Hunk.getOffset(), Hunk.getLength(), + Hunk.getReplacementText()); + Repl_C2_vec.push_back(Replacement); + }); + + auto C1 = groupReplcementsByFile(Repl_C1); + auto C2 = groupReplcementsByFile(Repl_C2_vec); + for (const auto &[FilePath, Repls_C1] : C1) { + auto ItC2 = C2.find(FilePath); + if (ItC2 == C2.end()) { + // No replacements in C2 for this file, just add C1 + Result.insert(Result.end(), Repls_C1.begin(), Repls_C1.end()); + continue; } + // Merge replacements in C1 and C2 for this file + auto Repls_C2 = ItC2->second; + auto Merged = ReplacementMerger::merge(Repls_C1, Repls_C2); + Result.insert(Result.end(), Merged.begin(), Merged.end()); } - Result.insert(Result.end(), Repl_C2_vec.begin(), Repl_C2_vec.end()); + return Result; } @@ -429,9 +626,11 @@ std::map> reMigrationMerge( Repl_C2.MoveFileHunks.empty() && "Repl_C2 should only have ModifiyFileHunks."); // Merge Repl_C1 and Repl_C2. If has conflict, keep repl from Repl_C2. - // TODO: Repl_C1 has name like file1.cpp, file2.cpp, file3.cu, file4.cuh - // but Repl_C2 has name like file1.cpp, file2.cpp.dp.cpp, file3.dp.cpp, file4.dp.hpp - // we need convert the filename in Repl_C2 to CUDA style + // 1. Repl_C1 has name like file1.cpp, file2.cpp, file3.cu, file4.cuh + // but Repl_C2 has name like file1.cpp, file2.cpp.dp.cpp, file3.dp.cpp, + // file4.dp.hpp. We need convert the filename in Repl_C2 to CUDA style + // 2. Repl_C1 is based on CUDA code, Repl_C2 is based on the migrated SYCL + // code, the result Repl_C should based on the original CUDA code. std::vector Repl_C = mergeC1AndC2(Repl_C1, Repl_C2, FileNameMap); // Convert vector in Repl_A to map for quick lookup. @@ -462,6 +661,7 @@ std::map> reMigrationMerge( // Get Repl_C_y std::map Repl_C_y; + for (const auto &Repl : Repl_C) { // The gitdiff changes are line-based while clang replacements are // character-based. So here assume there is no overlap (only repl totally @@ -475,14 +675,17 @@ std::map> reMigrationMerge( } // Check if the replacement is in a deleted part. + bool HasConflict = false; for (const auto &Part : It->second) { - if (hasConflict(Repl, Replacement(Repl.getFilePath(), Part.first, - Part.second, ""))) + if (HasConflict = + hasConflict(Repl, Replacement(Repl.getFilePath(), Part.first, + Part.second, ""))) break; } - llvm::cantFail(Repl_C_y[Repl.getFilePath().str()].add( - Replacement(Repl.getFilePath().str(), Repl.getOffset(), - Repl.getLength(), Repl.getReplacementText()))); + if (!HasConflict) + llvm::cantFail(Repl_C_y[Repl.getFilePath().str()].add( + Replacement(Repl.getFilePath().str(), Repl.getOffset(), + Repl.getLength(), Repl.getReplacementText()))); } // Shift Repl_C_y with Repl_A(ModifiedParts) diff --git a/clang/tools/dpct/gitdiff2yaml/gitdiff2yaml.cpp b/clang/tools/dpct/gitdiff2yaml/gitdiff2yaml.cpp index 6228182237f7..4d0c6ca9a8ee 100644 --- a/clang/tools/dpct/gitdiff2yaml/gitdiff2yaml.cpp +++ b/clang/tools/dpct/gitdiff2yaml/gitdiff2yaml.cpp @@ -210,7 +210,10 @@ std::vector parseDiff(const std::string &diffOutput, HunkContext HC; std::vector CurrentOldFileOffset; - while (std::getline(iss, line)) { + // Don't use std::getline as condition of the while loop, because it will + // return false if the last line only containing EOF. + while (iss.good()) { + std::getline(iss, line); if (startsWith(line, "diff --git")) { HC.FastForward = false; continue; diff --git a/clang/unittests/DPCT/ReMigrationTest.cpp b/clang/unittests/DPCT/ReMigrationTest.cpp index cf59301d3ac2..831443249117 100644 --- a/clang/unittests/DPCT/ReMigrationTest.cpp +++ b/clang/unittests/DPCT/ReMigrationTest.cpp @@ -184,11 +184,11 @@ TEST_F(ReMigrationTest2, splitReplInOrderToNotCrossLines) { // (zz\nyy) =>(xx) // // Splitted repls: - // (ccc\n) =>(jjj\nkkk) + // (ccc\n) =>(jjj\nkkkhhhhiii\n) // (dddeeeefff\n) => "" - // (ggg) => "" - // (zz\n) => (xx) - // (yy) => "" + // (ggghhhhiii\n) => "" + // (zz\n) => (xxyyyyyyyy\n) + // (yyyyyyyyyy) => "" getLineStringHook = this->getLineStringUnittest; getLineNumberHook = this->getLineNumberUnittest; @@ -196,10 +196,11 @@ TEST_F(ReMigrationTest2, splitReplInOrderToNotCrossLines) { std::vector Repls = {Replacement("file1.cpp", 7, 18, "jjj\nkkk"), Replacement("file1.cpp", 41, 5, "xx")}; std::vector Expected = { - Replacement("file1.cpp", 7, 4, "jjj\nkkk"), - Replacement("file1.cpp", 11, 11, ""), Replacement("file1.cpp", 22, 3, ""), - Replacement("file1.cpp", 41, 3, "xx"), - Replacement("file1.cpp", 44, 2, "")}; + Replacement("file1.cpp", 7, 4, "jjj\nkkkhhhhiii\n"), + Replacement("file1.cpp", 11, 11, ""), + Replacement("file1.cpp", 22, 11, ""), + Replacement("file1.cpp", 41, 3, "xxyyyyyyyy\n"), + Replacement("file1.cpp", 44, 11, "")}; auto Result = splitReplInOrderToNotCrossLines(Repls); std::sort(Result.begin(), Result.end()); EXPECT_EQ(Expected, Result); @@ -275,12 +276,13 @@ eee bb ccc Replacement("file1.cpp", 84, 18, "ddd\neee")}; std::map Expected = {{1, "aaa zzz ccc\n"}, {2, "ppp aaa bb ccc\n"}, - {4, "aaa yyy ccc\n"}, - {6, "aaa bb ccq\nqqq"}, - {7, "aa bb ccc\n"}, - {8, "aaa bb ddd\neee"}, + {4, "aaa yyy ccc\naaa bb ccc\n"}, + {5, ""}, + {6, "aaa bb ccq\nqqqaa bb ccc\n"}, + {7, ""}, + {8, "aaa bb ddd\neee bb ccc\n"}, {9, ""}, - {10, " bb ccc\n"}}; + {10, ""}}; auto Result = convertReplcementsLineString(Repls); EXPECT_EQ(Expected, Result); } @@ -325,46 +327,48 @@ TEST_F(ReMigrationTest3, mergeMapsByLine) { } TEST_F(ReMigrationTest1, mergeC1AndC2) { + // clang-format off + // original file: +/* +0123456789 +0123456789 +0123456789 +0123456789 +0123456789 +*/ + // migrated file: +/* +0123456789aaa +bbb12345678ccc789 +0123456789 +*/ + // updated file: +/* +0123zzz456789aaa +bbyyy2345678xxxc78www0123456789 +*/ + // clang-format on std::vector Repl_C1 = { Replacement("file1.cu", 10, 0, "aaa"), Replacement("file1.cu", 11, 1, "bbb"), Replacement("file1.cu", 20, 20, "ccc"), - Replacement("file2.cu", 20, 20, "zzz"), - Replacement("file2.cu", 40, 1, "yyy"), - Replacement("file3.cpp", 0, 1, "a"), - Replacement("file4.cpp", 2, 1, "b"), }; GitDiffChanges Repl_C2; Repl_C2.ModifyFileHunks = { - Replacement("file1.dp.cpp", 10, 0, "ddd"), - Replacement("file1.dp.cpp", 10, 2, "eee"), - Replacement("file1.dp.cpp", 40, 2, "fff"), - Replacement("file2.dp.cpp", 21, 3, "xxx"), - Replacement("file2.dp.cpp", 41, 1, "www"), - Replacement("file3.cpp.dp.cpp", 10, 1, "a"), - Replacement("file4.cpp", 12, 1, "b"), + Replacement("file1.dp.cpp", 30, 2, "www"), + Replacement("file1.dp.cpp", 25, 2, "xxx"), + Replacement("file1.dp.cpp", 16, 2, "yyy"), + Replacement("file1.dp.cpp", 4, 0, "zzz"), }; const std::map FileNameMap = { - {"file1.dp.cpp", "file1.cu"}, - {"file2.dp.cpp", "file2.cu"}, - {"file3.cpp.dp.cpp", "file3.cpp"}, - {"file4.cpp", "file4.cpp"}}; + {"file1.dp.cpp", "file1.cu"}}; std::vector Result = mergeC1AndC2(Repl_C1, Repl_C2, FileNameMap); - std::vector Expected = { - Replacement("file1.cu", 10, 0, "aaa"), - Replacement("file1.cu", 10, 0, "ddd"), - Replacement("file1.cu", 10, 2, "eee"), - Replacement("file1.cu", 20, 20, "ccc"), - Replacement("file1.cu", 40, 2, "fff"), - Replacement("file2.cu", 21, 3, "xxx"), - Replacement("file2.cu", 40, 1, "yyy"), - Replacement("file2.cu", 41, 1, "www"), - Replacement("file3.cpp", 0, 1, "a"), - Replacement("file4.cpp", 2, 1, "b"), - Replacement("file3.cpp", 10, 1, "a"), - Replacement("file4.cpp", 12, 1, "b"), - }; + std::vector Expected = {Replacement("file1.cu", 4, 0, "zzz"), + Replacement("file1.cu", 10, 0, "aaa"), + Replacement("file1.cu", 11, 2, "bbyyy"), + Replacement("file1.cu", 20, 20, "xxxc"), + Replacement("file1.cu", 42, 2, "www")}; std::sort(Result.begin(), Result.end()); std::sort(Expected.begin(), Expected.end()); @@ -382,14 +386,13 @@ TEST_F(ReMigrationTest1, mergeC1AndC2) { } } -#if 0 class ReMigrationTest4 : public ::testing::Test { protected: - inline static std::vector CUDACodeV1 = {}; + inline static std::vector CUDACodeV2Vec = {}; inline static std::vector LineOffsets = {}; void SetUp() override { // clang-format off - const std::string CUDACode = R"(#include + const std::string CUDACodeV2 = R"(#include #define CUDA_CHECK(call) \ do { \ @@ -402,42 +405,48 @@ class ReMigrationTest4 : public ::testing::Test { } while (0) void foo() { - float *f; - CUDA_CHECK(cudaMalloc(&f, 100 * sizeof(float))); float *g; CUDA_CHECK(cudaMalloc(&g, 100 * sizeof(float))); - cudaMemcpy(f, g, 100 * sizeof(float), cudaMemcpyDeviceToDevice); + float *h; + CUDA_CHECK(cudaMalloc(&h, 100 * sizeof(float))); cudaDeviceSynchronize(); - cudaFree(f); cudaFree(g); + cudaFree(h); } )"; // clang-format on - std::istringstream ISS(CUDACode); + std::istringstream ISS(CUDACodeV2); std::string Line; - while (std::getline(ISS, Line)) { - if (ISS.eof() && Line.empty()) + // TODO: This code only considers the last line is empty (file ending by a \n). + // What if the last line is not empty? + while (true) { + bool LastLine = false; + if (!std::getline(ISS, Line)) + LastLine = true; + else + Line += '\n'; + LineOffsets.push_back(LineOffsets.empty() + ? 0 + : LineOffsets.back() + + CUDACodeV2Vec.back().size()); + CUDACodeV2Vec.push_back(Line); + if (LastLine) break; - Line += '\n'; - LineOffsets.push_back(LineOffsets.empty() ? 0 - : LineOffsets.back() + - CUDACodeV1.back().size()); - CUDACodeV1.push_back(Line); } LineOffsets.insert(LineOffsets.begin(), 0); } - void TearDown() override { CUDACodeV1.clear(); } + void TearDown() override { CUDACodeV2Vec.clear(); } static StringRef getLineStringUnittest(clang::tooling::UnifiedPath FilePath, unsigned LineNumber) { - return StringRef(CUDACodeV1[LineNumber - 1]); + return StringRef(CUDACodeV2Vec[LineNumber - 1]); } static unsigned getLineNumberUnittest(clang::tooling::UnifiedPath FilePath, unsigned Offset) { auto Iter = - std::upper_bound(LineOffsets.begin(), LineOffsets.end(), Offset); + std::upper_bound(LineOffsets.begin() + 1, LineOffsets.end(), Offset); if (Iter == LineOffsets.end()) return LineOffsets.size(); - return std::distance(LineOffsets.begin(), Iter); + return std::distance(LineOffsets.begin() + 1, Iter); } static unsigned getLineBeginOffsetUnittest(clang::tooling::UnifiedPath FilePath, @@ -451,6 +460,9 @@ TEST_F(ReMigrationTest4, reMigrationMerge) { getLineNumberHook = this->getLineNumberUnittest; getLineBeginOffsetHook = this->getLineBeginOffsetUnittest; + const std::map FileNameMap = { + {"test.dp.cpp", "test.cu"}}; + std::vector Repl_C1 = { Replacement("test.cu", 0, 0, "#include \n#include \n"), @@ -482,14 +494,17 @@ TEST_F(ReMigrationTest4, reMigrationMerge) { "exc.what() << \"Exception caught at file:\" << __FILE__ << " "\", line:\" << __LINE__ << std::endl;\n std::exit(1);\n}")}; - std::vector Repl_C2 = { - Replacement("test.dp.cpp", 70, 527, "void foo() {\n"), - Replacement("test.dp.cpp", 716, 76, + GitDiffChanges Repl_C2; + Repl_C2.ModifyFileHunks = { + Replacement("test.dp.cpp", 70, 526, "void foo() {\n"), + Replacement("test.dp.cpp", 715, 76, " f = sycl::malloc_device(100, q_ct1);\n"), - Replacement("test.dp.cpp", 804, 76, - " g = sycl::malloc_device(100, q_ct1);\n")}; + Replacement("test.dp.cpp", 803, 76, + " g = sycl::malloc_device(100, q_ct1);\n"), + Replacement("test.dp.cpp", 1017, 163, "")}; - std::vector Repl_A = { + GitDiffChanges Repl_A; + Repl_A.ModifyFileHunks = { Replacement("test.cu", 696, 63, ""), Replacement( "test.cu", 822, 67, @@ -526,6 +541,79 @@ TEST_F(ReMigrationTest4, reMigrationMerge) { "exc.what() << \"Exception caught at file:\" << __FILE__ << " "\", line:\" << __LINE__ << std::endl;\n std::exit(1);\n}")}; - ASSERT_EQ(true, false); // Placeholder for actual test logic + std::map> ResultMap = + reMigrationMerge(Repl_A, Repl_B, Repl_C1, Repl_C2, FileNameMap); + + std::vector Result = ResultMap.begin()->second; + std::vector Expected = { + Replacement("test.cu", 0, 19, R"(#include +#include +#include +)"), + Replacement("test.cu", 20, 81, R"xxx(<<<<<<< +/* +DPCT1009:0: SYCL reports errors using exceptions and does not use error codes. Please replace the "get_error_string_dummy(...)" with a real error-handling function. +*/ +#define CUDA_CHECK(call) \ +======= +void foo() { + dpct::device_ext &dev_ct1 = dpct::get_current_device(); + sycl::queue &q_ct1 = dev_ct1.in_order_queue(); +>>>>>>> +)xxx"), + Replacement("test.cu", 101, 81, R"()"), + Replacement("test.cu", 182, 486, R"(<<<<<<< + dpct::err0 err = call; \ + \ +======= +>>>>>>> +)"), + Replacement("test.cu", 668, 14, R"()"), + Replacement("test.cu", 682, 1, R"()"), + Replacement("test.cu", 683, 13, R"(<<<<<<< +void foo() try { + dpct::device_ext &dev_ct1 = dpct::get_current_device(); + sycl::queue &q_ct1 = dev_ct1.in_order_queue(); +======= +>>>>>>> +)"), + Replacement("test.cu", 708, 51, R"(<<<<<<< + CUDA_CHECK(DPCT_CHECK_ERROR(g = sycl::malloc_device(100, q_ct1))); +======= + g = sycl::malloc_device(100, q_ct1); + float *h; +>>>>>>> +)"), + Replacement("test.cu", 759, 12, R"()"), + Replacement( + "test.cu", 771, 51, + R"( CUDA_CHECK(DPCT_CHECK_ERROR(h = sycl::malloc_device(100, q_ct1))); +)"), + Replacement("test.cu", 822, 27, R"( dev_ct1.queues_wait_and_throw(); +)"), + Replacement("test.cu", 849, 15, R"( dpct::dpct_free(g, q_ct1); +)"), + Replacement("test.cu", 864, 15, R"( dpct::dpct_free(h, q_ct1); +)"), + Replacement("test.cu", 879, 2, R"(} +catch (sycl::exception const &exc) { + std::cerr << exc.what() << "Exception caught at file:" << __FILE__ << ", line:" << __LINE__ << std::endl; + std::exit(1); +} +)")}; + std::sort(Result.begin(), Result.end()); + std::sort(Expected.begin(), Expected.end()); + + ASSERT_EQ(Expected.size(), Result.size()); + size_t Num = Expected.size(); + auto ExpectedIt = Expected.begin(); + auto ResultIt = Result.begin(); + for (size_t i = 0; i < Num; ++i) { + EXPECT_EQ(ExpectedIt->getFilePath(), ResultIt->getFilePath()); + EXPECT_EQ(ExpectedIt->getOffset(), ResultIt->getOffset()); + EXPECT_EQ(ExpectedIt->getLength(), ResultIt->getLength()); + EXPECT_EQ(ExpectedIt->getReplacementText(), ResultIt->getReplacementText()); + ExpectedIt++; + ResultIt++; + } } -#endif From ecd447b5094fb33a45416b7553605f3229ba7993 Mon Sep 17 00:00:00 2001 From: "Jiang, Zhiwei" Date: Thu, 26 Jun 2025 15:21:19 +0800 Subject: [PATCH 39/53] Small update Signed-off-by: Jiang, Zhiwei --- clang/lib/DPCT/IncMigration/ReMigration.cpp | 287 ++++++++++---------- 1 file changed, 148 insertions(+), 139 deletions(-) diff --git a/clang/lib/DPCT/IncMigration/ReMigration.cpp b/clang/lib/DPCT/IncMigration/ReMigration.cpp index 04bfe7200b95..434e72b13b7f 100644 --- a/clang/lib/DPCT/IncMigration/ReMigration.cpp +++ b/clang/lib/DPCT/IncMigration/ReMigration.cpp @@ -389,158 +389,166 @@ static bool hasConflict(const Replacement &R1, const Replacement &R2) { return false; } -class ReplacementMerger { -public: - static std::vector merge(const std::vector &A, - const std::vector &B) { - // 1. 建立A组替换后的offset映射 - auto [a_map, a_modified] = buildAMapping(A); - std::string FilePath = A[0].getFilePath().str(); - - std::vector final_repls; - std::vector a_repls; - for (const auto &repl : a_modified) { - a_repls.push_back( - {FilePath, std::get<0>(repl), std::get<1>(repl), std::get<2>(repl)}); +namespace { +// Repl group A is the replacements generated by the first run of dpct +// Repl group B is the replacements written by the user manaully based on the +// SYCL code generated by the first run of dpct. + +// We name the original CUDA code TEXT0, the migrated code TEXT1, the manually +// fixed SYCL code TEXT2. + +// Generate a mapping: (offset in TEXT1) -> (offset in TEXT0). + +// Build the mappings based on the replacements in group A. +std::pair>, + std::vector>> +buildMapping(const std::vector &A) { + std::vector> OffsetMap; + std::vector> ModifiedRangeList; + unsigned OrigPos = 0; + unsigned MigratedCodePos = 0; + + auto SortedA = A; + std::sort(SortedA.begin(), SortedA.end()); + + for (const auto &Repl : SortedA) { + // Add mapping before repl + if (Repl.getOffset() > OrigPos) { + unsigned len = Repl.getOffset() - OrigPos; + OffsetMap.emplace_back(MigratedCodePos, OrigPos); + MigratedCodePos += len; + OrigPos += len; } - std::vector b_repls = B; - std::sort( - a_repls.begin(), - a_repls.end()); // The length in replA is based on original text, so for - // below calculation which is based on modified text, we - // need to use text.size() instead. - std::sort(b_repls.begin(), b_repls.end()); - - auto IterA = a_repls.begin(); - auto IterB = b_repls.begin(); - - std::optional> + + // record the repl range + ModifiedRangeList.emplace_back(MigratedCodePos, Repl.getLength(), + Repl.getReplacementText().str()); + + // Add mapping after repl + OffsetMap.emplace_back(MigratedCodePos, Repl.getOffset()); + MigratedCodePos += Repl.getReplacementText().size(); + OrigPos += Repl.getLength(); + } + + // the last fragment + OffsetMap.emplace_back(MigratedCodePos, OrigPos); + return {OffsetMap, ModifiedRangeList}; +} + +// Map the offset in TEXT1 to the offset in TEXT0. +unsigned +mapToOriginalOffset(unsigned MigratedCodeOffset, + const std::vector> &Mapping) { + auto It = std::upper_bound( + Mapping.begin(), Mapping.end(), std::make_pair(MigratedCodeOffset, 0), + [](const auto &a, const auto &b) { return a.first < b.first; }); + + if (It != Mapping.begin()) + --It; + return It->second + (MigratedCodeOffset - It->first); +} + +std::vector mergeC1AndC2Impl(const std::vector &A, + const std::vector &B) { + // Build 2 mappings + auto [OffsetMap, ModifiedRangeList] = buildMapping(A); + std::string FilePath = A[0].getFilePath().str(); + + std::vector Result; + std::vector AGroupRepls; + for (const auto &repl : ModifiedRangeList) { + AGroupRepls.push_back( + {FilePath, std::get<0>(repl), std::get<1>(repl), std::get<2>(repl)}); + } + std::vector BGroupRepls = B; + std::sort( + AGroupRepls.begin(), + AGroupRepls.end()); // The length in replA is based on original text, so + // for below calculation which is based on modified + // text, we need to use text.size() instead. + std::sort(BGroupRepls.begin(), BGroupRepls.end()); + + auto IterA = AGroupRepls.begin(); + auto IterB = BGroupRepls.begin(); + + std::optional> + UnfinishedRepl = std::nullopt; + while (IterA != AGroupRepls.end() || IterB != BGroupRepls.end()) { + if (UnfinishedRepl) { + unsigned Off = std::get<0>(*UnfinishedRepl); + unsigned Len = std::get<1>(*UnfinishedRepl); + std::string Text = std::get<2>(*UnfinishedRepl); + if ((Off + Len) < IterA->getOffset() && + (Off + Len) < IterB->getOffset()) { + // Finished + Result.push_back({FilePath, mapToOriginalOffset(Off, OffsetMap), + mapToOriginalOffset(Off + Len, OffsetMap) - + mapToOriginalOffset(Off, OffsetMap), + Text}); UnfinishedRepl = std::nullopt; - while (IterA != a_repls.end() || IterB != b_repls.end()) { - if (UnfinishedRepl) { - unsigned Off = std::get<0>(*UnfinishedRepl); - unsigned Len = std::get<1>(*UnfinishedRepl); - std::string Text = std::get<2>(*UnfinishedRepl); - if ((Off + Len) < IterA->getOffset() && - (Off + Len) < IterB->getOffset()) { - // Finished - final_repls.push_back( - {FilePath, mapToOriginal(Off, a_map), - mapToOriginal(Off + Len, a_map) - mapToOriginal(Off, a_map), - Text}); - UnfinishedRepl = std::nullopt; - continue; - } - if ((Off + Len) >= IterB->getOffset()) { - // Unfinished repl overlapping with next B repl. - // We process B first because it has higher priority. - std::get<1>(*UnfinishedRepl) = - IterB->getLength() + IterB->getOffset() - Off; - std::get<2>(*UnfinishedRepl) = - Text.substr(0, IterB->getOffset() - Off) + - IterB->getReplacementText().str(); - ++IterB; - continue; - } - // (Off + Len) >= IterA->getOffset() - if ((Off + Len) >= - (IterA->getOffset() + IterA->getReplacementText().size())) { - // case 1: totally cover IterA - ++IterA; - continue; - } - // case 2: partially cover IterA - std::get<1>(*UnfinishedRepl) = - IterA->getReplacementText().size() + IterA->getOffset() - Off; - std::get<2>(*UnfinishedRepl) = - Text + IterA->getReplacementText().str().substr(Off + Len - - IterA->getOffset()); - ++IterA; continue; } - if (IterA == a_repls.end()) { - // only left B group replacements - final_repls.push_back( - {FilePath, mapToOriginal(IterB->getOffset(), a_map), - mapToOriginal(IterB->getOffset() + IterB->getLength(), a_map) - - mapToOriginal(IterB->getOffset(), a_map), - IterB->getReplacementText()}); + if ((Off + Len) >= IterB->getOffset()) { + // Unfinished repl overlapping with next B repl. + // We process B first because it has higher priority. + std::get<1>(*UnfinishedRepl) = + IterB->getLength() + IterB->getOffset() - Off; + std::get<2>(*UnfinishedRepl) = + Text.substr(0, IterB->getOffset() - Off) + + IterB->getReplacementText().str(); ++IterB; continue; } - if (IterB == b_repls.end()) { - // only left A group replacements - final_repls.push_back(*IterA); + // (Off + Len) >= IterA->getOffset() + if ((Off + Len) >= + (IterA->getOffset() + IterA->getReplacementText().size())) { + // case 1: totally cover IterA ++IterA; continue; } - if (IterA->getOffset() < IterB->getOffset()) { - UnfinishedRepl = std::make_tuple(IterA->getOffset(), - IterA->getReplacementText().size(), - IterA->getReplacementText().str()); - ++IterA; - } else { - UnfinishedRepl = std::make_tuple(IterB->getOffset(), IterB->getLength(), - IterB->getReplacementText().str()); - ++IterB; - } + // case 2: partially cover IterA + std::get<1>(*UnfinishedRepl) = + IterA->getReplacementText().size() + IterA->getOffset() - Off; + std::get<2>(*UnfinishedRepl) = + Text + IterA->getReplacementText().str().substr(Off + Len - + IterA->getOffset()); + ++IterA; + continue; } - - std::sort(final_repls.begin(), final_repls.end()); - return final_repls; - } - -private: - // 建立A组替换映射关系 - static std::pair>, - std::vector>> - buildAMapping(const std::vector &A) { - std::vector> offset_map; - std::vector> modified_ranges; - unsigned orig_pos = 0; - unsigned modified_pos = 0; - - auto sorted_A = A; - std::sort(sorted_A.begin(), sorted_A.end()); - - for (const auto &repl : sorted_A) { - // 添加替换前的区间 - if (repl.getOffset() > orig_pos) { - unsigned len = repl.getOffset() - orig_pos; - offset_map.emplace_back(modified_pos, orig_pos); - modified_pos += len; - orig_pos += len; - } - - // 记录修改区域 - modified_ranges.emplace_back(modified_pos, repl.getLength(), - repl.getReplacementText().str()); - - // 添加替换后的映射 - offset_map.emplace_back(modified_pos, repl.getOffset()); - modified_pos += repl.getReplacementText().size(); - orig_pos += repl.getLength(); + if (IterA == AGroupRepls.end()) { + // only left B group replacements + Result.push_back( + {FilePath, mapToOriginalOffset(IterB->getOffset(), OffsetMap), + mapToOriginalOffset(IterB->getOffset() + IterB->getLength(), + OffsetMap) - + mapToOriginalOffset(IterB->getOffset(), OffsetMap), + IterB->getReplacementText()}); + ++IterB; + continue; + } + if (IterB == BGroupRepls.end()) { + // only left A group replacements + Result.push_back(*IterA); + ++IterA; + continue; + } + if (IterA->getOffset() < IterB->getOffset()) { + UnfinishedRepl = std::make_tuple(IterA->getOffset(), + IterA->getReplacementText().size(), + IterA->getReplacementText().str()); + ++IterA; + } else { + UnfinishedRepl = std::make_tuple(IterB->getOffset(), IterB->getLength(), + IterB->getReplacementText().str()); + ++IterB; } - - // 添加最后一段 - offset_map.emplace_back(modified_pos, orig_pos); - return {offset_map, modified_ranges}; } - // 将修改后offset映射回原始offset - static unsigned - mapToOriginal(unsigned modified_offset, - const std::vector> &mapping) { - auto it = std::upper_bound( - mapping.begin(), mapping.end(), std::make_pair(modified_offset, 0), - [](const auto &a, const auto &b) { return a.first < b.first; }); - - if (it != mapping.begin()) - --it; - return it->second + (modified_offset - it->first); - } -}; + std::sort(Result.begin(), Result.end()); + return Result; +} +} // namespace // Merge Repl_C1 and Repl_C2. If has conflict, keep repl from Repl_C2. std::vector mergeC1AndC2( @@ -570,7 +578,7 @@ std::vector mergeC1AndC2( } // Merge replacements in C1 and C2 for this file auto Repls_C2 = ItC2->second; - auto Merged = ReplacementMerger::merge(Repls_C1, Repls_C2); + auto Merged = mergeC1AndC2Impl(Repls_C1, Repls_C2); Result.insert(Result.end(), Merged.begin(), Merged.end()); } @@ -622,6 +630,7 @@ std::map> reMigrationMerge( const std::vector &Repl_C1, const GitDiffChanges &Repl_C2, const std::map &FileNameMap) { + // TODO: is this assumption true? Will the manual fix delete/add/move file? assert(Repl_C2.AddFileHunks.empty() && Repl_C2.DeleteFileHunks.empty() && Repl_C2.MoveFileHunks.empty() && "Repl_C2 should only have ModifiyFileHunks."); From 29b373e7226e57973b134978d673d31802104bfd Mon Sep 17 00:00:00 2001 From: "Jiang, Zhiwei" Date: Fri, 27 Jun 2025 09:23:23 +0800 Subject: [PATCH 40/53] Refine the conflict mark Signed-off-by: Jiang, Zhiwei --- clang/lib/DPCT/IncMigration/ReMigration.cpp | 34 +++++++++++++++------ clang/unittests/DPCT/ReMigrationTest.cpp | 5 +-- 2 files changed, 25 insertions(+), 14 deletions(-) diff --git a/clang/lib/DPCT/IncMigration/ReMigration.cpp b/clang/lib/DPCT/IncMigration/ReMigration.cpp index 434e72b13b7f..5411b48f7b5f 100644 --- a/clang/lib/DPCT/IncMigration/ReMigration.cpp +++ b/clang/lib/DPCT/IncMigration/ReMigration.cpp @@ -205,8 +205,11 @@ ddddd // In other words, if there is no conflict, the repl is correct since there is // \n after the repl. While the conflict mark assumes that the repl is endding // with \n so it can at the beginning of each line, this is the problem. -// So we need to merge the left part of the last line into the repl (the first -// line). +// So the solution is: +// 1. If the newtext is not ending with \n, we merge the left part of the last +// line into the repl (the first line). +// 2. If the newtext is ending with \n, we still keep the last line at the last +// line. std::vector splitReplInOrderToNotCrossLines(const std::vector &InRepls) { std::string FilePath = InRepls[0].getFilePath().str(); @@ -228,11 +231,15 @@ splitReplInOrderToNotCrossLines(const std::vector &InRepls) { unsigned CurrentOffset = StartOffset; // The first line - // We need merge the left part of the last line into the replacement + // We need merge the left part of the last line into the replacement if the + // newText is not ending with \n. + bool IsFisrtLineEndingWithNL = Repl.getReplacementText().ends_with('\n'); unsigned LineEndOffset = getLineBeginOffset(FilePath, StartLine + 1); unsigned FirstLineLength = LineEndOffset - StartOffset; Replacement ReplFisrtLine(FilePath, CurrentOffset, FirstLineLength, - Repl.getReplacementText()); + Repl.getReplacementText()); + if (IsFisrtLineEndingWithNL) + Result.push_back(ReplFisrtLine); CurrentOffset += FirstLineLength; // middle lines @@ -246,12 +253,19 @@ splitReplInOrderToNotCrossLines(const std::vector &InRepls) { // The last line std::string LastLineStr = getLineString(FilePath, EndLine).str(); unsigned LastLineLength = EndOffset - CurrentOffset; - Result.emplace_back(Repl.getFilePath(), ReplFisrtLine.getOffset(), - ReplFisrtLine.getLength(), - ReplFisrtLine.getReplacementText().str() + - LastLineStr.substr(LastLineLength)); - Result.emplace_back(Repl.getFilePath(), CurrentOffset, LastLineStr.size(), - ""); + if (IsFisrtLineEndingWithNL) { + if (LastLineLength > 0) { + Result.emplace_back(Repl.getFilePath(), CurrentOffset, LastLineLength, + ""); + } + } else { + Result.emplace_back(Repl.getFilePath(), ReplFisrtLine.getOffset(), + ReplFisrtLine.getLength(), + ReplFisrtLine.getReplacementText().str() + + LastLineStr.substr(LastLineLength)); + Result.emplace_back(Repl.getFilePath(), CurrentOffset, LastLineStr.size(), + ""); + } } return Result; diff --git a/clang/unittests/DPCT/ReMigrationTest.cpp b/clang/unittests/DPCT/ReMigrationTest.cpp index 831443249117..683e2d820c99 100644 --- a/clang/unittests/DPCT/ReMigrationTest.cpp +++ b/clang/unittests/DPCT/ReMigrationTest.cpp @@ -276,8 +276,7 @@ eee bb ccc Replacement("file1.cpp", 84, 18, "ddd\neee")}; std::map Expected = {{1, "aaa zzz ccc\n"}, {2, "ppp aaa bb ccc\n"}, - {4, "aaa yyy ccc\naaa bb ccc\n"}, - {5, ""}, + {4, "aaa yyy ccc\n"}, {6, "aaa bb ccq\nqqqaa bb ccc\n"}, {7, ""}, {8, "aaa bb ddd\neee bb ccc\n"}, @@ -581,10 +580,8 @@ void foo() try { CUDA_CHECK(DPCT_CHECK_ERROR(g = sycl::malloc_device(100, q_ct1))); ======= g = sycl::malloc_device(100, q_ct1); - float *h; >>>>>>> )"), - Replacement("test.cu", 759, 12, R"()"), Replacement( "test.cu", 771, 51, R"( CUDA_CHECK(DPCT_CHECK_ERROR(h = sycl::malloc_device(100, q_ct1))); From 487096c532d624594fce9e2cd75c0f28d2991259 Mon Sep 17 00:00:00 2001 From: "Jiang, Zhiwei" Date: Thu, 3 Jul 2025 08:50:57 +0800 Subject: [PATCH 41/53] Try to integrate, but has bug Signed-off-by: Jiang, Zhiwei --- clang/lib/DPCT/DPCT.cpp | 4 +- clang/lib/DPCT/FileGenerator/GenFiles.cpp | 40 +++++++++++++- .../DPCT/IncMigration/ExternalReplacement.cpp | 5 +- .../DPCT/IncMigration/ExternalReplacement.h | 2 +- clang/lib/DPCT/IncMigration/ReMigration.cpp | 52 ++++++++++++------- clang/lib/DPCT/IncMigration/ReMigration.h | 1 + 6 files changed, 81 insertions(+), 23 deletions(-) diff --git a/clang/lib/DPCT/DPCT.cpp b/clang/lib/DPCT/DPCT.cpp index 4e134efd34eb..a2397a619c5f 100644 --- a/clang/lib/DPCT/DPCT.cpp +++ b/clang/lib/DPCT/DPCT.cpp @@ -79,7 +79,7 @@ using namespace clang::tooling; using namespace llvm::cl; extern bool isDPCT; - +extern bool ReMigrationReady; namespace clang { namespace tooling { UnifiedPath getFormatSearchPath(); @@ -1402,7 +1402,7 @@ int runDPCT(int argc, const char **argv) { dpctExit(MigrationSucceeded, false); } - tryLoadingUpstreamChangesAndUserChanges(); + ReMigrationReady = tryLoadingUpstreamChangesAndUserChanges(); ReplTy ReplCUDA, ReplSYCL; volatile int RunCount = 0; diff --git a/clang/lib/DPCT/FileGenerator/GenFiles.cpp b/clang/lib/DPCT/FileGenerator/GenFiles.cpp index 327644e4c299..3cd76417b3e7 100644 --- a/clang/lib/DPCT/FileGenerator/GenFiles.cpp +++ b/clang/lib/DPCT/FileGenerator/GenFiles.cpp @@ -48,6 +48,7 @@ extern DpctOption ProcessAll; extern DpctOption BuildScriptFile; extern DpctOption GenBuildScript; extern std::map ErrorCnt; +bool ReMigrationReady = false; namespace clang { namespace tooling { @@ -508,7 +509,7 @@ void applyPatternRewriter(const std::string &InputString, } int writeReplacementsToFiles( - ReplTy &Replset, Rewriter &Rewrite, const std::string &Folder, + ReplTy &Replset2, Rewriter &Rewrite, const std::string &Folder, clang::tooling::UnifiedPath &InRoot, std::vector &MainSrcFilesInfo, std::unordered_map &MainSrcFileMap, @@ -523,6 +524,42 @@ int writeReplacementsToFiles( volatile ProcessStatus status = MigrationSucceeded; clang::tooling::UnifiedPath OutPath; + ReplTy Replset; + if (ReMigrationReady) { + std::vector Repl_B; + std::vector Repl_C1; + // TODO: test only + std::map + FileNameMap; + for (const auto &Entry : Replset2) { + clang::tooling::UnifiedPath Path = Entry.first; + rewriteFileName(Path); + FileNameMap[Path] = Entry.first; + for (const auto &Repl : Entry.second) { + Repl_B.push_back(Repl); + } + } + + for (const auto &Repl : clang::dpct::getLastMigration().Replacements) { + Repl_C1.push_back(Repl); + } + std::map> Result = + clang::dpct::reMigrationMerge(clang::dpct::getUpstreamChanges(), Repl_B, + Repl_C1, clang::dpct::getUserChanges(), + FileNameMap); + for (const auto &Entry : Result) { + clang::tooling::Replacements Repls; + for (const auto &Repl : Entry.second) { + llvm::cantFail(Repls.add(Repl)); + } + Replset.insert(std::make_pair(Entry.first, Repls)); + } + } else { + // If not re-migration, just use the original Replset. + Replset = Replset2; + } + for (auto &Entry : Replset) { OutPath = StringRef(DpctGlobalInfo::removeSymlinks( Rewrite.getSourceMgr().getFileManager(), Entry.first)); @@ -649,6 +686,7 @@ int writeReplacementsToFiles( // We have written a migrated file; Update the output file path info OutFilePath2InFilePath[OutPath.getCanonicalPath().str()] = Entry.first; } + Replset2 = Replset; return status; } diff --git a/clang/lib/DPCT/IncMigration/ExternalReplacement.cpp b/clang/lib/DPCT/IncMigration/ExternalReplacement.cpp index 93ee7f8bbb72..4561f56f61e0 100644 --- a/clang/lib/DPCT/IncMigration/ExternalReplacement.cpp +++ b/clang/lib/DPCT/IncMigration/ExternalReplacement.cpp @@ -109,7 +109,10 @@ int loadTUFromYaml(const clang::tooling::UnifiedPath &Input, } } - if (IsSrcFileChanged) { + // TODO: test only + const bool ReMigrationEnabled = true; + + if (IsSrcFileChanged && !ReMigrationEnabled) { // File doesn't appear to be a header change description. Ignore it. TU = clang::tooling::TranslationUnitReplacements(); return -1; diff --git a/clang/lib/DPCT/IncMigration/ExternalReplacement.h b/clang/lib/DPCT/IncMigration/ExternalReplacement.h index 99c9376394b1..388a33f52d91 100644 --- a/clang/lib/DPCT/IncMigration/ExternalReplacement.h +++ b/clang/lib/DPCT/IncMigration/ExternalReplacement.h @@ -48,7 +48,7 @@ void mergeAndUniqueReps( clang::tooling::Replacements &Replaces, const std::vector &PreRepls); -void tryLoadingUpstreamChangesAndUserChanges(); +bool tryLoadingUpstreamChangesAndUserChanges(); } // namespace dpct } // namespace clang diff --git a/clang/lib/DPCT/IncMigration/ReMigration.cpp b/clang/lib/DPCT/IncMigration/ReMigration.cpp index 5411b48f7b5f..8f7bee743715 100644 --- a/clang/lib/DPCT/IncMigration/ReMigration.cpp +++ b/clang/lib/DPCT/IncMigration/ReMigration.cpp @@ -53,6 +53,7 @@ namespace clang::dpct { using namespace clang::tooling; static GitDiffChanges UpstreamChanges; static GitDiffChanges UserChanges; +static TranslationUnitReplacements LastMigration; AddFileHunk::AddFileHunk(std::string NewFilePath) : Hunk(AddFile), NewFilePath(UnifiedPath(NewFilePath).getCanonicalPath()) {} DeleteFileHunk::DeleteFileHunk(std::string OldFilePath) @@ -60,6 +61,7 @@ DeleteFileHunk::DeleteFileHunk(std::string OldFilePath) OldFilePath(UnifiedPath(OldFilePath).getCanonicalPath()) {} GitDiffChanges &getUpstreamChanges() { return UpstreamChanges; } GitDiffChanges &getUserChanges() { return UserChanges; } +TranslationUnitReplacements &getLastMigration() { return LastMigration; } static void dumpGitDiffChanges(const GitDiffChanges &GHC) { llvm::errs() << "GitDiffChanges:\n"; llvm::errs() << " ModifyFileHunks:\n"; @@ -89,20 +91,31 @@ static void dumpGitDiffChanges(const GitDiffChanges &GHC) { } } -void tryLoadingUpstreamChangesAndUserChanges() { +bool tryLoadingUpstreamChangesAndUserChanges() { + unsigned int Found = 0; llvm::SmallString<128> UpstreamChangesFilePath( DpctGlobalInfo::getInRoot().getCanonicalPath()); llvm::SmallString<128> UserChangesFilePath( DpctGlobalInfo::getInRoot().getCanonicalPath()); + llvm::SmallString<128> LastMigrationFilePath( + DpctGlobalInfo::getInRoot().getCanonicalPath()); llvm::sys::path::append(UpstreamChangesFilePath, "UpstreamChanges.yaml"); llvm::sys::path::append(UserChangesFilePath, "UserChanges.yaml"); + llvm::sys::path::append(LastMigrationFilePath, "LastMigration.yaml"); if (llvm::sys::fs::exists(UpstreamChangesFilePath)) { ::loadGDCFromYaml(UpstreamChangesFilePath, getUpstreamChanges()); + Found++; } if (llvm::sys::fs::exists(UserChangesFilePath)) { ::loadGDCFromYaml(UserChangesFilePath, getUserChanges()); + Found++; + } + if (llvm::sys::fs::exists(LastMigrationFilePath)) { + ::loadTUFromYaml(LastMigrationFilePath, getLastMigration()); + Found++; } + return (Found == 3) ? true : false; } /// Below 3 functions only used for merging Repl_B and repl_D. So they only @@ -489,12 +502,14 @@ std::vector mergeC1AndC2Impl(const std::vector &A, unsigned /*length*/, std::string>> UnfinishedRepl = std::nullopt; while (IterA != AGroupRepls.end() || IterB != BGroupRepls.end()) { + bool NoA = IterA == AGroupRepls.end(); + bool NoB = IterB == BGroupRepls.end(); if (UnfinishedRepl) { unsigned Off = std::get<0>(*UnfinishedRepl); unsigned Len = std::get<1>(*UnfinishedRepl); std::string Text = std::get<2>(*UnfinishedRepl); - if ((Off + Len) < IterA->getOffset() && - (Off + Len) < IterB->getOffset()) { + if ((NoA || (Off + Len) < IterA->getOffset()) && + (NoB || (Off + Len) < IterB->getOffset())) { // Finished Result.push_back({FilePath, mapToOriginalOffset(Off, OffsetMap), mapToOriginalOffset(Off + Len, OffsetMap) - @@ -503,7 +518,7 @@ std::vector mergeC1AndC2Impl(const std::vector &A, UnfinishedRepl = std::nullopt; continue; } - if ((Off + Len) >= IterB->getOffset()) { + if (!NoB && (Off + Len) >= IterB->getOffset()) { // Unfinished repl overlapping with next B repl. // We process B first because it has higher priority. std::get<1>(*UnfinishedRepl) = @@ -514,23 +529,24 @@ std::vector mergeC1AndC2Impl(const std::vector &A, ++IterB; continue; } - // (Off + Len) >= IterA->getOffset() - if ((Off + Len) >= - (IterA->getOffset() + IterA->getReplacementText().size())) { - // case 1: totally cover IterA + if (!NoA && (Off + Len) >= IterA->getOffset()) { + if ((Off + Len) >= + (IterA->getOffset() + IterA->getReplacementText().size())) { + // case 1: totally cover IterA + ++IterA; + continue; + } + // case 2: partially cover IterA + std::get<1>(*UnfinishedRepl) = + IterA->getReplacementText().size() + IterA->getOffset() - Off; + std::get<2>(*UnfinishedRepl) = + Text + IterA->getReplacementText().str().substr(Off + Len - + IterA->getOffset()); ++IterA; continue; } - // case 2: partially cover IterA - std::get<1>(*UnfinishedRepl) = - IterA->getReplacementText().size() + IterA->getOffset() - Off; - std::get<2>(*UnfinishedRepl) = - Text + IterA->getReplacementText().str().substr(Off + Len - - IterA->getOffset()); - ++IterA; - continue; } - if (IterA == AGroupRepls.end()) { + if (NoA) { // only left B group replacements Result.push_back( {FilePath, mapToOriginalOffset(IterB->getOffset(), OffsetMap), @@ -541,7 +557,7 @@ std::vector mergeC1AndC2Impl(const std::vector &A, ++IterB; continue; } - if (IterB == BGroupRepls.end()) { + if (NoB) { // only left A group replacements Result.push_back(*IterA); ++IterA; diff --git a/clang/lib/DPCT/IncMigration/ReMigration.h b/clang/lib/DPCT/IncMigration/ReMigration.h index e76a2fd21ee2..4d64498e4e66 100644 --- a/clang/lib/DPCT/IncMigration/ReMigration.h +++ b/clang/lib/DPCT/IncMigration/ReMigration.h @@ -94,6 +94,7 @@ struct GitDiffChanges { }; GitDiffChanges &getUpstreamChanges(); GitDiffChanges &getUserChanges(); +clang::tooling::TranslationUnitReplacements &getLastMigration(); clang::tooling::Replacements calculateUpdatedRanges(const clang::tooling::Replacements &Repls, From 5c74a69591b2219ef5005b8711e183e2ce15bd5c Mon Sep 17 00:00:00 2001 From: "Jiang, Zhiwei" Date: Thu, 3 Jul 2025 10:08:16 +0800 Subject: [PATCH 42/53] Some fix (other bugs are in gitdiff2yaml tool (double quote/single quote/escape for line ending ...)) Signed-off-by: Jiang, Zhiwei --- clang/lib/DPCT/IncMigration/ReMigration.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/clang/lib/DPCT/IncMigration/ReMigration.cpp b/clang/lib/DPCT/IncMigration/ReMigration.cpp index 8f7bee743715..890d04dcb2da 100644 --- a/clang/lib/DPCT/IncMigration/ReMigration.cpp +++ b/clang/lib/DPCT/IncMigration/ReMigration.cpp @@ -575,6 +575,17 @@ std::vector mergeC1AndC2Impl(const std::vector &A, } } + // TODO: figure out why we need this? + if (UnfinishedRepl) { + Result.push_back( + {FilePath, mapToOriginalOffset(std::get<0>(*UnfinishedRepl), OffsetMap), + mapToOriginalOffset(std::get<0>(*UnfinishedRepl) + + std::get<1>(*UnfinishedRepl), + OffsetMap) - + mapToOriginalOffset(std::get<0>(*UnfinishedRepl), OffsetMap), + std::get<2>(*UnfinishedRepl)}); + } + std::sort(Result.begin(), Result.end()); return Result; } From 3c4260854f612ee9eb973b4e43e3bd7444ac0367 Mon Sep 17 00:00:00 2001 From: "Jiang, Zhiwei" Date: Thu, 3 Jul 2025 14:07:16 +0800 Subject: [PATCH 43/53] intergrate Signed-off-by: Jiang, Zhiwei --- clang/include/clang/DPCT/DPCTOptions.inc | 6 + clang/lib/DPCT/AnalysisInfo.cpp | 1 + clang/lib/DPCT/AnalysisInfo.h | 3 + clang/lib/DPCT/DPCT.cpp | 1 + .../DPCT/IncMigration/ExternalReplacement.cpp | 5 +- clang/test/dpct/autocomplete.c | 1 + .../dpct/help_option_check/lin/help_all.txt | 1 + .../test/dpct/remigration/LastMigration.yaml | 369 ++++++++++++++++++ .../dpct/remigration/UpstreamChanges.yaml | 19 + clang/test/dpct/remigration/UserChanges.yaml | 27 ++ clang/test/dpct/remigration/expect.txt | 132 +++++++ clang/test/dpct/remigration/src.txt | 68 ++++ clang/test/dpct/remigration/test.cpp | 10 + 13 files changed, 639 insertions(+), 4 deletions(-) create mode 100644 clang/test/dpct/remigration/LastMigration.yaml create mode 100644 clang/test/dpct/remigration/UpstreamChanges.yaml create mode 100644 clang/test/dpct/remigration/UserChanges.yaml create mode 100644 clang/test/dpct/remigration/expect.txt create mode 100644 clang/test/dpct/remigration/src.txt create mode 100644 clang/test/dpct/remigration/test.cpp diff --git a/clang/include/clang/DPCT/DPCTOptions.inc b/clang/include/clang/DPCT/DPCTOptions.inc index afa05039ad58..cab2f9ee3690 100644 --- a/clang/include/clang/DPCT/DPCTOptions.inc +++ b/clang/include/clang/DPCT/DPCTOptions.inc @@ -958,6 +958,12 @@ DPCT_FLAG_OPTION( "--out-root directory. Default: off."), llvm::cl::cat(CtHelpCatAll), llvm::cl::cat(CtHelpCatCodeGen)) +DPCT_FLAG_OPTION( + ReMigration, clang::dpct::DpctOptionClass::OC_Attribute, + DPCT_OPTION_ACTIONS(clang::dpct::DpctActionKind::DAK_Migration), + "remigration", llvm::cl::desc("Enable remigration. Default: off."), + llvm::cl::cat(CtHelpCatAll), llvm::cl::cat(CtHelpCatCodeGen)) + DPCT_FLAG_OPTION( PathToHelperFunction, clang::dpct::DpctOptionClass::OC_Action, DPCT_OPTION_ACTIONS(clang::dpct::DpctActionKind::DAK_Help), diff --git a/clang/lib/DPCT/AnalysisInfo.cpp b/clang/lib/DPCT/AnalysisInfo.cpp index 36cd3cfe5405..5456db8aed1f 100644 --- a/clang/lib/DPCT/AnalysisInfo.cpp +++ b/clang/lib/DPCT/AnalysisInfo.cpp @@ -2519,6 +2519,7 @@ unsigned DpctGlobalInfo::ExperimentalFlag = 0; unsigned DpctGlobalInfo::HelperFuncPreferenceFlag = 0; bool DpctGlobalInfo::AnalysisModeFlag = false; bool DpctGlobalInfo::UseSYCLCompatFlag = false; +bool DpctGlobalInfo::ReMigrationFlag = false; bool DpctGlobalInfo::CVersionCUDALaunchUsedFlag = false; unsigned int DpctGlobalInfo::ColorOption = 1; std::unordered_map> diff --git a/clang/lib/DPCT/AnalysisInfo.h b/clang/lib/DPCT/AnalysisInfo.h index 06958a3785ec..d69bda639258 100644 --- a/clang/lib/DPCT/AnalysisInfo.h +++ b/clang/lib/DPCT/AnalysisInfo.h @@ -1362,6 +1362,8 @@ class DpctGlobalInfo { static bool isCVersionCUDALaunchUsed() { return CVersionCUDALaunchUsedFlag; } static void setUseSYCLCompat(bool Flag = true) { UseSYCLCompatFlag = Flag; } static bool useSYCLCompat() { return UseSYCLCompatFlag; } + static void setReMigration(bool Flag = true) { ReMigrationFlag = Flag; } + static bool useReMigration() { return ReMigrationFlag; } static bool useEnqueueBarrier() { return getUsingExtensionDE( DPCPPExtensionsDefaultEnabled::ExtDE_EnqueueBarrier); @@ -1699,6 +1701,7 @@ class DpctGlobalInfo { static unsigned HelperFuncPreferenceFlag; static bool AnalysisModeFlag; static bool UseSYCLCompatFlag; + static bool ReMigrationFlag; static bool CVersionCUDALaunchUsedFlag; static unsigned int ColorOption; static std::unordered_map> diff --git a/clang/lib/DPCT/DPCT.cpp b/clang/lib/DPCT/DPCT.cpp index a2397a619c5f..914db44c3db5 100644 --- a/clang/lib/DPCT/DPCT.cpp +++ b/clang/lib/DPCT/DPCT.cpp @@ -1245,6 +1245,7 @@ int runDPCT(int argc, const char **argv) { DpctGlobalInfo::setOptimizeMigrationFlag(OptimizeMigration.getValue()); DpctGlobalInfo::setSYCLFileExtension(SYCLFileExtension); DpctGlobalInfo::setUseSYCLCompat(UseSYCLCompat); + DpctGlobalInfo::setReMigration(ReMigration); StopOnParseErrTooling = StopOnParseErr; InRootTooling = InRootPath; diff --git a/clang/lib/DPCT/IncMigration/ExternalReplacement.cpp b/clang/lib/DPCT/IncMigration/ExternalReplacement.cpp index 4561f56f61e0..659c0e8243bf 100644 --- a/clang/lib/DPCT/IncMigration/ExternalReplacement.cpp +++ b/clang/lib/DPCT/IncMigration/ExternalReplacement.cpp @@ -109,10 +109,7 @@ int loadTUFromYaml(const clang::tooling::UnifiedPath &Input, } } - // TODO: test only - const bool ReMigrationEnabled = true; - - if (IsSrcFileChanged && !ReMigrationEnabled) { + if (IsSrcFileChanged && !DpctGlobalInfo::useReMigration()) { // File doesn't appear to be a header change description. Ignore it. TU = clang::tooling::TranslationUnitReplacements(); return -1; diff --git a/clang/test/dpct/autocomplete.c b/clang/test/dpct/autocomplete.c index 9a80e0d3401a..f2f86238a865 100644 --- a/clang/test/dpct/autocomplete.c +++ b/clang/test/dpct/autocomplete.c @@ -45,6 +45,7 @@ // DASH-NEXT: --output-verbosity= // DASH-NEXT: --process-all // DASH-NEXT: --query-api-mapping +// DASH-NEXT: --remigration // DASH-NEXT: --report-file-prefix // DASH-NEXT: --report-format= // DASH-NEXT: --report-only diff --git a/clang/test/dpct/help_option_check/lin/help_all.txt b/clang/test/dpct/help_option_check/lin/help_all.txt index 0a759cf82505..b5542709ca41 100644 --- a/clang/test/dpct/help_option_check/lin/help_all.txt +++ b/clang/test/dpct/help_option_check/lin/help_all.txt @@ -106,6 +106,7 @@ All DPCT options to the --out-root directory. The --in-root option should be explicitly specified. Default: off. --query-api-mapping= - Query functionally compatible SYCL API to migrate CUDA API. + --remigration - Enable remigration. Default: off. --report-file-prefix= - Specify the prefix for the migration report file names. The full file name will have a suffix derived from the report-type, and an extension derived from the report-format. For example: .apis.csv or .stats.log. If this option is not diff --git a/clang/test/dpct/remigration/LastMigration.yaml b/clang/test/dpct/remigration/LastMigration.yaml new file mode 100644 index 000000000000..adc597226381 --- /dev/null +++ b/clang/test/dpct/remigration/LastMigration.yaml @@ -0,0 +1,369 @@ +--- +MainSourceFile: '/home/zhiwei/testfolder/test/MainSrcFiles_placehold' +Replacements: + - FilePath: 'test.cu' + Offset: 0 + Length: 26 + ReplacementText: "#include \n#include \n" + ConstantFlag: '' + ConstantOffset: 0 + InitStr: '' + NewHostVarName: '' + BlockLevelFormatFlag: false + - FilePath: 'test.cu' + Offset: 45 + Length: 0 + ReplacementText: "/*\nDPCT1009:3: SYCL reports errors using exceptions and does not use error codes. Please replace the \"get_error_string_dummy(...)\" with a real error-handling function.\n*/\n" + ConstantFlag: '' + ConstantOffset: 0 + InitStr: '' + NewHostVarName: '' + BlockLevelFormatFlag: false + - FilePath: 'test.cu' + Offset: 211 + Length: 11 + ReplacementText: 'dpct::err0' + ConstantFlag: '' + ConstantOffset: 0 + InitStr: '' + NewHostVarName: '' + BlockLevelFormatFlag: false + - FilePath: 'test.cu' + Offset: 292 + Length: 325 + ReplacementText: '' + ConstantFlag: '' + ConstantOffset: 0 + InitStr: '' + NewHostVarName: '' + BlockLevelFormatFlag: false + - FilePath: 'test.cu' + Offset: 708 + Length: 11 + ReplacementText: '' + ConstantFlag: '' + ConstantOffset: 0 + InitStr: '' + NewHostVarName: '' + BlockLevelFormatFlag: false + - FilePath: 'test.cu' + Offset: 782 + Length: 0 + ReplacementText: "auto item_ct1 = sycl::ext::oneapi::this_work_item::get_nd_item<3>();\n " + ConstantFlag: '' + ConstantOffset: 0 + InitStr: '' + NewHostVarName: '' + BlockLevelFormatFlag: false + - FilePath: 'test.cu' + Offset: 792 + Length: 10 + ReplacementText: 'item_ct1.get_group(2)' + ConstantFlag: '' + ConstantOffset: 0 + InitStr: '' + NewHostVarName: '' + BlockLevelFormatFlag: false + - FilePath: 'test.cu' + Offset: 805 + Length: 10 + ReplacementText: 'item_ct1.get_local_range(2)' + ConstantFlag: '' + ConstantOffset: 0 + InitStr: '' + NewHostVarName: '' + BlockLevelFormatFlag: false + - FilePath: 'test.cu' + Offset: 818 + Length: 11 + ReplacementText: 'item_ct1.get_local_id(2)' + ConstantFlag: '' + ConstantOffset: 0 + InitStr: '' + NewHostVarName: '' + BlockLevelFormatFlag: false + - FilePath: 'test.cu' + Offset: 931 + Length: 0 + ReplacementText: ' try ' + ConstantFlag: '' + ConstantOffset: 0 + InitStr: '' + NewHostVarName: '' + BlockLevelFormatFlag: false + - FilePath: 'test.cu' + Offset: 932 + Length: 0 + ReplacementText: "\n dpct::device_ext &dev_ct1 = dpct::get_current_device();\n sycl::queue &q_ct1 = dev_ct1.in_order_queue();" + ConstantFlag: '' + ConstantOffset: 0 + InitStr: '' + NewHostVarName: '' + BlockLevelFormatFlag: false + - FilePath: 'test.cu' + Offset: 975 + Length: 42 + ReplacementText: 'DPCT_CHECK_ERROR(input_d = sycl::malloc_device(size, q_ct1))' + ConstantFlag: '' + ConstantOffset: 0 + InitStr: '' + NewHostVarName: '' + BlockLevelFormatFlag: false + - FilePath: 'test.cu' + Offset: 1033 + Length: 43 + ReplacementText: 'DPCT_CHECK_ERROR(output_d = sycl::malloc_device(size, q_ct1))' + ConstantFlag: '' + ConstantOffset: 0 + InitStr: '' + NewHostVarName: '' + BlockLevelFormatFlag: false + - FilePath: 'test.cu' + Offset: 1099 + Length: 71 + ReplacementText: 'DPCT_CHECK_ERROR(q_ct1.memcpy(input_d, data, size * sizeof(float)).wait())' + ConstantFlag: '' + ConstantOffset: 0 + InitStr: '' + NewHostVarName: '' + BlockLevelFormatFlag: false + - FilePath: 'test.cu' + Offset: 1251 + Length: 0 + ReplacementText: " /*\n DPCT1049:0: The work-group size passed to the SYCL kernel may exceed the limit. To get the device limit, query info::device::max_work_group_size. Adjust the work-group size if needed.\n */\n" + ConstantFlag: '' + ConstantOffset: 0 + InitStr: '' + NewHostVarName: '' + BlockLevelFormatFlag: false + - FilePath: 'test.cu' + Offset: 1253 + Length: 64 + ReplacementText: "q_ct1.parallel_for(\n sycl::nd_range<3>(sycl::range<3>(1, 1, numBlocks) * sycl::range<3>(1, 1, blockSize), sycl::range<3>(1, 1, blockSize)), \n [=](sycl::nd_item<3> item_ct1) {\n scale_kernel(input_d, scale, output_d);\n });" + ConstantFlag: '' + ConstantOffset: 0 + InitStr: '' + NewHostVarName: '' + BlockLevelFormatFlag: true + - FilePath: 'test.cu' + Offset: 1317 + Length: 1 + ReplacementText: '' + ConstantFlag: '' + ConstantOffset: 0 + InitStr: '' + NewHostVarName: '' + BlockLevelFormatFlag: false + - FilePath: 'test.cu' + Offset: 1332 + Length: 23 + ReplacementText: 'DPCT_CHECK_ERROR(dev_ct1.queues_wait_and_throw())' + ConstantFlag: '' + ConstantOffset: 0 + InitStr: '' + NewHostVarName: '' + BlockLevelFormatFlag: false + - FilePath: 'test.cu' + Offset: 1358 + Length: 0 + ReplacementText: " /*\n DPCT1010:4: SYCL uses exceptions to report errors and does not use the error codes. The cudaGetLastError function call was replaced with 0. You need to rewrite this code.\n */\n" + ConstantFlag: '' + ConstantOffset: 0 + InitStr: '' + NewHostVarName: '' + BlockLevelFormatFlag: false + - FilePath: 'test.cu' + Offset: 1360 + Length: 11 + ReplacementText: 'dpct::err0' + ConstantFlag: '' + ConstantOffset: 0 + InitStr: '' + NewHostVarName: '' + BlockLevelFormatFlag: false + - FilePath: 'test.cu' + Offset: 1378 + Length: 18 + ReplacementText: '0' + ConstantFlag: '' + ConstantOffset: 0 + InitStr: '' + NewHostVarName: '' + BlockLevelFormatFlag: false + - FilePath: 'test.cu' + Offset: 1398 + Length: 0 + ReplacementText: " /*\n DPCT1000:2: Error handling if-stmt was detected but could not be rewritten.\n */\n" + ConstantFlag: '' + ConstantOffset: 0 + InitStr: '' + NewHostVarName: '' + BlockLevelFormatFlag: false + - FilePath: 'test.cu' + Offset: 1411 + Length: 11 + ReplacementText: '0' + ConstantFlag: '' + ConstantOffset: 0 + InitStr: '' + NewHostVarName: '' + BlockLevelFormatFlag: false + - FilePath: 'test.cu' + Offset: 1426 + Length: 0 + ReplacementText: " /*\n DPCT1009:5: SYCL reports errors using exceptions and does not use error codes. Please replace the \"get_error_string_dummy(...)\" with a real error-handling function.\n */\n" + ConstantFlag: '' + ConstantOffset: 0 + InitStr: '' + NewHostVarName: '' + BlockLevelFormatFlag: false + - FilePath: 'test.cu' + Offset: 1481 + Length: 23 + ReplacementText: 'dpct::get_error_string_dummy(err)' + ConstantFlag: '' + ConstantOffset: 0 + InitStr: '' + NewHostVarName: '' + BlockLevelFormatFlag: false + - FilePath: 'test.cu' + Offset: 1507 + Length: 0 + ReplacementText: " /*\n DPCT1001:1: The statement could not be removed.\n */\n" + ConstantFlag: '' + ConstantOffset: 0 + InitStr: '' + NewHostVarName: '' + BlockLevelFormatFlag: false + - FilePath: 'test.cu' + Offset: 1511 + Length: 17 + ReplacementText: 'dpct::dpct_free(input_d, q_ct1)' + ConstantFlag: '' + ConstantOffset: 0 + InitStr: '' + NewHostVarName: '' + BlockLevelFormatFlag: false + - FilePath: 'test.cu' + Offset: 1534 + Length: 18 + ReplacementText: 'dpct::dpct_free(output_d, q_ct1)' + ConstantFlag: '' + ConstantOffset: 0 + InitStr: '' + NewHostVarName: '' + BlockLevelFormatFlag: false + - FilePath: 'test.cu' + Offset: 1596 + Length: 98 + ReplacementText: 'DPCT_CHECK_ERROR(q_ct1.memcpy(output, output_d, size * sizeof(float)).wait())' + ConstantFlag: '' + ConstantOffset: 0 + InitStr: '' + NewHostVarName: '' + BlockLevelFormatFlag: false + - FilePath: 'test.cu' + Offset: 1710 + Length: 17 + ReplacementText: 'DPCT_CHECK_ERROR(dpct::dpct_free(input_d, q_ct1))' + ConstantFlag: '' + ConstantOffset: 0 + InitStr: '' + NewHostVarName: '' + BlockLevelFormatFlag: false + - FilePath: 'test.cu' + Offset: 1743 + Length: 18 + ReplacementText: 'DPCT_CHECK_ERROR(dpct::dpct_free(output_d, q_ct1))' + ConstantFlag: '' + ConstantOffset: 0 + InitStr: '' + NewHostVarName: '' + BlockLevelFormatFlag: false + - FilePath: 'test.cu' + Offset: 1765 + Length: 0 + ReplacementText: "\ncatch (sycl::exception const &exc) {\n std::cerr << exc.what() << \"Exception caught at file:\" << __FILE__ << \", line:\" << __LINE__ << std::endl;\n std::exit(1);\n}" + ConstantFlag: '' + ConstantOffset: 0 + InitStr: '' + NewHostVarName: '' + BlockLevelFormatFlag: false +MainSourceFilesDigest: + - MainSourceFile: '/home/zhiwei/testfolder/test/test.cu' + Digest: 63232f272cda678b14a86a4243a28f63 + HasCUDASyntax: true +DpctVersion: 21.0.0 +SDKVersionMajor: '12' +SDKVersionMinor: '8' +MainHelperFileName: '' +USMLevel: '' +FeatureMap: {} +CompileTargets: {} +OptionMap: + AnalysisScopePath: + Value: '' + ValueVec: + - '/home/zhiwei/testfolder/test' + Specified: false + AsyncHandler: + Value: 'false' + Specified: false + BuildScript: + Value: '0' + Specified: false + CodePinEnabled: + Value: 'false' + Specified: false + CommentsEnabled: + Value: 'false' + Specified: false + CompilationsDir: + Value: '' + Specified: false + CtadEnabled: + Value: 'false' + Specified: false + EnablepProfiling: + Value: 'false' + Specified: false + ExperimentalFlag: + Value: '0' + Specified: false + ExplicitNamespace: + Value: '20' + Specified: false + ExtensionDDFlag: + Value: '0' + Specified: false + ExtensionDEFlag: + Value: '4294967295' + Specified: false + HelperFuncPreferenceFlag: + Value: '0' + Specified: false + NDRangeDim: + Value: '3' + Specified: false + NoDRYPattern: + Value: 'false' + Specified: false + OptimizeMigration: + Value: 'false' + Specified: false + ProcessAll: + Value: 'false' + Specified: false + RuleFile: + Value: '' + Specified: false + SyclNamedLambda: + Value: 'false' + Specified: false + UseSYCLCompat: + Value: 'false' + Specified: false + UsmLevel: + Value: '1' + Specified: false +... diff --git a/clang/test/dpct/remigration/UpstreamChanges.yaml b/clang/test/dpct/remigration/UpstreamChanges.yaml new file mode 100644 index 000000000000..d3cde8719110 --- /dev/null +++ b/clang/test/dpct/remigration/UpstreamChanges.yaml @@ -0,0 +1,19 @@ +--- +ModifyFileHunks: + - FilePath: 'test.cu' + Offset: 26 + Length: 0 + ReplacementText: "#include \"cublas_v2.h\"\n" + - FilePath: 'test.cu' + Offset: 708 + Length: 0 + ReplacementText: "#define CHECK_CUBLAS(call) \\\n do { \\\n cublasStatus_t err = call; \\\n if (err != CUBLAS_STATUS_SUCCESS) { \\\n fprintf(stderr, \"CUBLAS error in %s at line %d: %d\\n\", __FILE__, \\\n __LINE__, err); \\\n exit(EXIT_FAILURE); \\\n } \\\n } while (0)\n\n" + - FilePath: 'test.cu' + Offset: 869 + Length: 0 + ReplacementText: "void scale_cublas(float *data, float scale, float *output, int size) {\n cublasHandle_t handle;\n CHECK_CUBLAS(cublasCreate(&handle));\n float alpha = scale;\n CHECK_CUBLAS(cublasSscal(handle, size, &alpha, data, 1));\n CHECK_CUDA(cudaDeviceSynchronize());\n CHECK_CUBLAS(cublasDestroy(handle));\n}\n\ntemplate\n" + - FilePath: 'test.cu' + Offset: 1174 + Length: 404 + ReplacementText: " if (use_cublas) {\n scale_cublas(input_d, scale, output_d, size);\n } else {\n int blockSize = 256;\n int numBlocks = (size + blockSize - 1) / blockSize;\n scale_kernel<<>>(input_d, scale, output_d);\n CHECK_CUDA(cudaDeviceSynchronize());\n cudaError_t err = cudaGetLastError();\n if (err != cudaSuccess) {\n fprintf(stderr, \"CUDA kernel launch failed: %s\\n\",\n cudaGetErrorString(err));\n cudaFree(input_d);\n cudaFree(output_d);\n exit(EXIT_FAILURE);\n }\n" +... diff --git a/clang/test/dpct/remigration/UserChanges.yaml b/clang/test/dpct/remigration/UserChanges.yaml new file mode 100644 index 000000000000..dc8297aa27c5 --- /dev/null +++ b/clang/test/dpct/remigration/UserChanges.yaml @@ -0,0 +1,27 @@ +--- +ModifyFileHunks: + - FilePath: 'test.dp.cpp' + Offset: 69 + Length: 508 + ReplacementText: "" + - FilePath: 'test.dp.cpp' + Offset: 709 + Length: 92 + ReplacementText: " int idx = item_ct1.get_group(2) * item_ct1.get_local_range(2) +\n item_ct1.get_local_id(2);\n" + - FilePath: 'test.dp.cpp' + Offset: 839 + Length: 69 + ReplacementText: "void scale(float *data, float scale, float *output, int size) try {\n" + - FilePath: 'test.dp.cpp' + Offset: 1044 + Length: 264 + ReplacementText: " input_d = sycl::malloc_device(size, q_ct1);\n output_d = sycl::malloc_device(size, q_ct1);\n q_ct1.memcpy(input_d, data, size * sizeof(float)).wait();\n" + - FilePath: 'test.dp.cpp' + Offset: 1386 + Length: 1252 + ReplacementText: " q_ct1.parallel_for(sycl::nd_range<3>(sycl::range<3>(1, 1, numBlocks) *\n sycl::range<3>(1, 1, blockSize),\n sycl::range<3>(1, 1, blockSize)),\n [=](sycl::nd_item<3> item_ct1) {\n scale_kernel(input_d, scale, output_d);\n });\n dev_ct1.queues_wait_and_throw();\n" + - FilePath: 'test.dp.cpp' + Offset: 2639 + Length: 371 + ReplacementText: " q_ct1.memcpy(output, output_d, size * sizeof(float)).wait();\n dpct::dpct_free(input_d, q_ct1);\n dpct::dpct_free(output_d, q_ct1);\n} catch (sycl::exception const &exc) {\n std::cerr << exc.what() << \"Exception caught at file:\" << __FILE__\n << \", line:\" << __LINE__ << std::endl;\n" +... diff --git a/clang/test/dpct/remigration/expect.txt b/clang/test/dpct/remigration/expect.txt new file mode 100644 index 000000000000..67abf638b0ee --- /dev/null +++ b/clang/test/dpct/remigration/expect.txt @@ -0,0 +1,132 @@ +<<<<<<< +#include +#include +#include +#include +======= +#include +#include +>>>>>>> + +<<<<<<< +/* +DPCT1009:1: SYCL reports errors using exceptions and does not use error codes. Please replace the "get_error_string_dummy(...)" with a real error-handling function. +*/ +#define CHECK_CUDA(call) \ +======= +void scale_kernel(float *data, float scale, float *output) { +>>>>>>> +<<<<<<< + dpct::err0 err = call; \ + \ +======= +>>>>>>> +<<<<<<< + int err = call; \ + if (err != 0) { \ +======= +>>>>>>> +<<<<<<< +void scale_kernel(float *data, float scale, float *output) { + auto item_ct1 = sycl::ext::oneapi::this_work_item::get_nd_item<3>(); + int idx = item_ct1.get_group(2) * item_ct1.get_local_range(2) + item_ct1.get_local_id(2); +======= + auto item_ct1 = sycl::ext::oneapi::this_work_item::get_nd_item<3>(); + int idx = item_ct1.get_group(2) * item_ct1.get_local_range(2) + + item_ct1.get_local_id(2); +>>>>>>> + output[idx] = data[idx] * scale; +} + +void scale_cublas(float *data, float scale, float *output, int size) try { + dpct::blas::descriptor_ptr handle; + CHECK_CUBLAS(DPCT_CHECK_ERROR(handle = new dpct::blas::descriptor())); + float alpha = scale; + CHECK_CUBLAS(DPCT_CHECK_ERROR(oneapi::mkl::blas::column_major::scal(handle->get_queue(), size, alpha, data, 1))); + CHECK_CUDA(DPCT_CHECK_ERROR(dpct::get_current_device().queues_wait_and_throw())); + CHECK_CUBLAS(DPCT_CHECK_ERROR(delete (handle))); +} +catch (sycl::exception const &exc) { + std::cerr << exc.what() << "Exception caught at file:" << __FILE__ << ", line:" << __LINE__ << std::endl; + std::exit(1); +} + +template +<<<<<<< +void scale(float *data, float scale, float *output, int size) try { + dpct::device_ext &dev_ct1 = dpct::get_current_device(); + sycl::queue &q_ct1 = dev_ct1.in_order_queue(); +======= +void scale(float *data, float scale, float *output, int size) try { + dpct::device_ext &dev_ct1 = dpct::get_current_device(); + sycl::queue &q_ct1 = dev_ct1.in_order_queue(); +>>>>>>> + float *input_d, *output_d; +<<<<<<< + CHECK_CUDA(DPCT_CHECK_ERROR(input_d = sycl::malloc_device(size, q_ct1))); + CHECK_CUDA(DPCT_CHECK_ERROR(output_d = sycl::malloc_device(size, q_ct1))); +======= + input_d = sycl::malloc_device(size, q_ct1); + output_d = sycl::malloc_device(size, q_ct1); + q_ct1.memcpy(input_d, data, size * sizeof(float)).wait(); +>>>>>>> +<<<<<<< + DPCT_CHECK_ERROR(q_ct1.memcpy(input_d, data, size * sizeof(float)).wait())); +======= +>>>>>>> + + if (use_cublas) { + scale_cublas(input_d, scale, output_d, size); + } else { + int blockSize = 256; + int numBlocks = (size + blockSize - 1) / blockSize; + /* + DPCT1049:0: The work-group size passed to the SYCL kernel may exceed the limit. To get the device limit, query info::device::max_work_group_size. Adjust the work-group size if needed. + */ + q_ct1.parallel_for( + sycl::nd_range<3>(sycl::range<3>(1, 1, numBlocks) * sycl::range<3>(1, 1, blockSize), sycl::range<3>(1, 1, blockSize)), + [=](sycl::nd_item<3> item_ct1) { + scale_kernel(input_d, scale, output_d); + }); + CHECK_CUDA(DPCT_CHECK_ERROR(dev_ct1.queues_wait_and_throw())); + /* + DPCT1010:4: SYCL uses exceptions to report errors and does not use the error codes. The cudaGetLastError function call was replaced with 0. You need to rewrite this code. + */ + dpct::err0 err = 0; + /* + DPCT1000:3: Error handling if-stmt was detected but could not be rewritten. + */ + if (err != 0) { + fprintf(stderr, "CUDA kernel launch failed: %s\n", + /* + DPCT1009:5: SYCL reports errors using exceptions and does not use error codes. Please replace the "get_error_string_dummy(...)" with a real error-handling function. + */ + dpct::get_error_string_dummy(err)); + /* + DPCT1001:2: The statement could not be removed. + */ + dpct::dpct_free(input_d, q_ct1); + dpct::dpct_free(output_d, q_ct1); + exit(EXIT_FAILURE); + } + } + +<<<<<<< + CHECK_CUDA(DPCT_CHECK_ERROR(q_ct1.memcpy(output, output_d, size * sizeof(float)).wait())); + CHECK_CUDA(DPCT_CHECK_ERROR(dpct::dpct_free(input_d, q_ct1))); + CHECK_CUDA(DPCT_CHECK_ERROR(dpct::dpct_free(output_d, q_ct1))); +} +catch (sycl::exception const &exc) { + std::cerr << exc.what() << "Exception caught at file:" << __FILE__ << ", line:" << __LINE__ << std::endl; + std::exit(1); +} +======= + q_ct1.memcpy(output, output_d, size * sizeof(float)).wait(); + dpct::dpct_free(input_d, q_ct1); + dpct::dpct_free(output_d, q_ct1); +} catch (sycl::exception const &exc) { + std::cerr << exc.what() << "Exception caught at file:" << __FILE__ + << ", line:" << __LINE__ << std::endl; + std::exit(1); +} +>>>>>>> diff --git a/clang/test/dpct/remigration/src.txt b/clang/test/dpct/remigration/src.txt new file mode 100644 index 000000000000..d8e0595dd594 --- /dev/null +++ b/clang/test/dpct/remigration/src.txt @@ -0,0 +1,68 @@ +#include "cuda_runtime.h" +#include "cublas_v2.h" +#include + +#define CHECK_CUDA(call) \ + do { \ + cudaError_t err = call; \ + if (err != cudaSuccess) { \ + fprintf(stderr, "CUDA error in %s at line %d: %s\n", __FILE__, __LINE__, \ + cudaGetErrorString(err)); \ + exit(EXIT_FAILURE); \ + } \ + } while (0) + +#define CHECK_CUBLAS(call) \ + do { \ + cublasStatus_t err = call; \ + if (err != CUBLAS_STATUS_SUCCESS) { \ + fprintf(stderr, "CUBLAS error in %s at line %d: %d\n", __FILE__, \ + __LINE__, err); \ + exit(EXIT_FAILURE); \ + } \ + } while (0) + +__global__ void scale_kernel(float *data, float scale, float *output) { + int idx = blockIdx.x * blockDim.x + threadIdx.x; + output[idx] = data[idx] * scale; +} + +void scale_cublas(float *data, float scale, float *output, int size) { + cublasHandle_t handle; + CHECK_CUBLAS(cublasCreate(&handle)); + float alpha = scale; + CHECK_CUBLAS(cublasSscal(handle, size, &alpha, data, 1)); + CHECK_CUDA(cudaDeviceSynchronize()); + CHECK_CUBLAS(cublasDestroy(handle)); +} + +template +void scale(float *data, float scale, float *output, int size) { + float *input_d, *output_d; + CHECK_CUDA(cudaMalloc(&input_d, size * sizeof(float))); + CHECK_CUDA(cudaMalloc(&output_d, size * sizeof(float))); + CHECK_CUDA( + cudaMemcpy(input_d, data, size * sizeof(float), cudaMemcpyHostToDevice)); + + if (use_cublas) { + scale_cublas(input_d, scale, output_d, size); + } else { + int blockSize = 256; + int numBlocks = (size + blockSize - 1) / blockSize; + scale_kernel<<>>(input_d, scale, output_d); + CHECK_CUDA(cudaDeviceSynchronize()); + cudaError_t err = cudaGetLastError(); + if (err != cudaSuccess) { + fprintf(stderr, "CUDA kernel launch failed: %s\n", + cudaGetErrorString(err)); + cudaFree(input_d); + cudaFree(output_d); + exit(EXIT_FAILURE); + } + } + + CHECK_CUDA(cudaMemcpy(output, output_d, size * sizeof(float), + cudaMemcpyDeviceToHost)); + CHECK_CUDA(cudaFree(input_d)); + CHECK_CUDA(cudaFree(output_d)); +} diff --git a/clang/test/dpct/remigration/test.cpp b/clang/test/dpct/remigration/test.cpp new file mode 100644 index 000000000000..9f868830311b --- /dev/null +++ b/clang/test/dpct/remigration/test.cpp @@ -0,0 +1,10 @@ +// UNSUPPORTED: system-windows + +// RUN: cd %T +// RUN: cp %S/LastMigration.yaml . +// RUN: cp %S/UpstreamChanges.yaml . +// RUN: cp %S/UserChanges.yaml . +// RUN: cp %S/src.txt test.cu + +// RUN: dpct --out-root out ./test.cu --format-range=none --remigration +// RUN: diff %S/expect.txt out/test.dp.cpp From fb4f64b0dc1639333d5db58b062e2e5d60cee4e3 Mon Sep 17 00:00:00 2001 From: "Jiang, Zhiwei" Date: Fri, 4 Jul 2025 15:25:24 +0800 Subject: [PATCH 44/53] Update Signed-off-by: Jiang, Zhiwei --- clang/lib/DPCT/IncMigration/ReMigration.cpp | 148 ++++++---- clang/lib/DPCT/IncMigration/ReMigration.h | 33 ++- clang/test/dpct/remigration/expect.txt | 5 - clang/unittests/DPCT/ReMigrationTest.cpp | 298 +++----------------- 4 files changed, 155 insertions(+), 329 deletions(-) diff --git a/clang/lib/DPCT/IncMigration/ReMigration.cpp b/clang/lib/DPCT/IncMigration/ReMigration.cpp index 890d04dcb2da..119dd0507fa8 100644 --- a/clang/lib/DPCT/IncMigration/ReMigration.cpp +++ b/clang/lib/DPCT/IncMigration/ReMigration.cpp @@ -162,11 +162,11 @@ static unsigned getLineBeginOffset(UnifiedPath FilePath, unsigned LineNumber) { /// \param Repls Replacements to apply. /// \param NewRepl Replacements before applying \p Repls. /// \return The result Repls. -clang::tooling::Replacements +std::vector calculateUpdatedRanges(const clang::tooling::Replacements &Repls, - const clang::tooling::Replacements &NewRepl) { + const std::vector &NewRepl) { // Assumption: no overlap in the each groups. - clang::tooling::Replacements Result; + std::vector Result; for (const auto &R : NewRepl) { // Check if the range (BOffset, EOffset - BOffset) is overlapped with any // repl in Repls @@ -188,8 +188,8 @@ calculateUpdatedRanges(const clang::tooling::Replacements &Repls, Repls.getShiftedCodePosition(R.getOffset() + R.getLength()); if (BOffset > EOffset) continue; - llvm::cantFail(Result.add(Replacement( - R.getFilePath(), BOffset, EOffset - BOffset, R.getReplacementText()))); + Result.push_back({R.getFilePath(), BOffset, EOffset - BOffset, + R.getReplacementText(), R.containsManualFix()}); } return Result; } @@ -223,16 +223,17 @@ ddddd // line into the repl (the first line). // 2. If the newtext is ending with \n, we still keep the last line at the last // line. -std::vector -splitReplInOrderToNotCrossLines(const std::vector &InRepls) { +std::vector +splitReplInOrderToNotCrossLines(const std::vector &InRepls) { std::string FilePath = InRepls[0].getFilePath().str(); - std::vector Result; + std::vector Result; for (const auto &Repl : InRepls) { unsigned StartOffset = Repl.getOffset(); unsigned EndOffset = StartOffset + Repl.getLength(); unsigned StartLine = getLineNumber(FilePath, StartOffset); unsigned EndLine = getLineNumber(FilePath, EndOffset); + bool Tag = Repl.containsManualFix(); if (StartLine == EndLine) { // Single line replacement @@ -249,8 +250,8 @@ splitReplInOrderToNotCrossLines(const std::vector &InRepls) { bool IsFisrtLineEndingWithNL = Repl.getReplacementText().ends_with('\n'); unsigned LineEndOffset = getLineBeginOffset(FilePath, StartLine + 1); unsigned FirstLineLength = LineEndOffset - StartOffset; - Replacement ReplFisrtLine(FilePath, CurrentOffset, FirstLineLength, - Repl.getReplacementText()); + TaggedReplacement ReplFisrtLine(FilePath, CurrentOffset, FirstLineLength, + Repl.getReplacementText(), Tag); if (IsFisrtLineEndingWithNL) Result.push_back(ReplFisrtLine); CurrentOffset += FirstLineLength; @@ -259,7 +260,8 @@ splitReplInOrderToNotCrossLines(const std::vector &InRepls) { for (unsigned Line = StartLine + 1; Line < EndLine; ++Line) { LineEndOffset = getLineBeginOffset(FilePath, Line + 1); unsigned LineLength = LineEndOffset - CurrentOffset; - Result.emplace_back(Repl.getFilePath(), CurrentOffset, LineLength, ""); + Result.emplace_back(Repl.getFilePath(), CurrentOffset, LineLength, "", + Tag); CurrentOffset += LineLength; } @@ -269,67 +271,78 @@ splitReplInOrderToNotCrossLines(const std::vector &InRepls) { if (IsFisrtLineEndingWithNL) { if (LastLineLength > 0) { Result.emplace_back(Repl.getFilePath(), CurrentOffset, LastLineLength, - ""); + "", Tag); } } else { Result.emplace_back(Repl.getFilePath(), ReplFisrtLine.getOffset(), ReplFisrtLine.getLength(), ReplFisrtLine.getReplacementText().str() + - LastLineStr.substr(LastLineLength)); + LastLineStr.substr(LastLineLength), + Tag); Result.emplace_back(Repl.getFilePath(), CurrentOffset, LastLineStr.size(), - ""); + "", Tag); } } return Result; } -std::map -convertReplcementsLineString(const std::vector &InRepls) { - std::vector Replacements = +std::map> +convertReplcementsLineString(const std::vector &InRepls) { + std::vector Replacements = splitReplInOrderToNotCrossLines(InRepls); UnifiedPath FilePath(InRepls[0].getFilePath()); // group replacement by line - std::map> ReplacementsByLine; + std::map> ReplacementsByLine; for (const auto &Repl : Replacements) { unsigned LineNum = getLineNumber(FilePath, Repl.getOffset()); ReplacementsByLine[LineNum].push_back(Repl); } // process each line - std::map Result; + std::map> Result; for (auto &[LineNum, Repls] : ReplacementsByLine) { std::sort(Repls.begin(), Repls.end()); std::string OriginalLineStr = getLineString(FilePath, LineNum).str(); unsigned LineStartOffset = getLineBeginOffset(FilePath, LineNum); std::string NewLineStr; + bool ContainsManualFix = false; unsigned Pos = 0; for (const auto &Repl : Repls) { unsigned StrOffset = Repl.getOffset() - LineStartOffset; NewLineStr += OriginalLineStr.substr(Pos, StrOffset - Pos); NewLineStr += Repl.getReplacementText().str(); Pos = StrOffset + Repl.getLength(); + ContainsManualFix = ContainsManualFix || Repl.containsManualFix(); } NewLineStr += OriginalLineStr.substr(Pos); - Result[LineNum] = NewLineStr; + Result[LineNum] = std::make_pair(NewLineStr, ContainsManualFix); } return Result; } static std::map -convertReplcementsLineString(const tooling::Replacements &Repls) { - std::vector ReplsVec; +convertReplcementsLineString(const std::vector &Repls) { + std::vector ReplsVec; for (const auto &R : Repls) { - ReplsVec.push_back(R); + ReplsVec.push_back({R, true}); + } + auto Temp = convertReplcementsLineString(ReplsVec); + std::map Result; + // Drop the tag info + for (const auto &[LineNum, LineStr] : Temp) { + Result[LineNum] = LineStr.first; } - return convertReplcementsLineString(ReplsVec); + return Result; } std::vector -mergeMapsByLine(const std::map &MapA, - const std::map &MapB, +mergeMapsByLine(const std::map + &MapA /*CUDA v2 code migration repls*/, + const std::map> + &MapB /*CUDA v1 code migration + manual fix repls*/, const UnifiedPath &FilePath) { auto genReplacement = [&](unsigned LineNumber, const std::string &LineContent) { @@ -345,7 +358,7 @@ mergeMapsByLine(const std::map &MapA, while (ItA != MapA.end() || ItB != MapB.end()) { if (ItA == MapA.end()) { - Result.push_back(genReplacement(ItB->first, ItB->second)); + Result.push_back(genReplacement(ItB->first, ItB->second.first)); ++ItB; } else if (ItB == MapB.end()) { Result.push_back(genReplacement(ItA->first, ItA->second)); @@ -354,17 +367,28 @@ mergeMapsByLine(const std::map &MapA, Result.push_back(genReplacement(ItA->first, ItA->second)); ++ItA; } else if (ItB->first < ItA->first) { - Result.push_back(genReplacement(ItB->first, ItB->second)); + Result.push_back(genReplacement(ItB->first, ItB->second.first)); ++ItB; } else { // ItB->first == ItA->first - if (ItB->second == ItA->second) { + // Conflict resolution + + if (ItB->second.first == ItA->second) { // No conflict, just add one of them Result.push_back(genReplacement(ItA->first, ItA->second)); ++ItA; ++ItB; continue; } + if (!ItB->second.second) { + // If ItB->second does not contain manual fix, we can use ItA->second + // directly + Result.push_back(genReplacement(ItA->first, ItA->second)); + ++ItA; + ++ItB; + continue; + } + // Conflict line(s) std::vector ConflictA; std::vector ConflictB; @@ -376,7 +400,7 @@ mergeMapsByLine(const std::map &MapA, while (ItA != MapA.end() && ItB != MapB.end() && CurrentLineNum == ItA->first && CurrentLineNum == ItB->first) { ConflictA.push_back(ItA->second); - ConflictB.push_back(ItB->second); + ConflictB.push_back(ItB->second.first); ConflictLength += getLineString(FilePath, ItA->first).size(); ++ItA; ++ItB; @@ -475,13 +499,14 @@ mapToOriginalOffset(unsigned MigratedCodeOffset, return It->second + (MigratedCodeOffset - It->first); } -std::vector mergeC1AndC2Impl(const std::vector &A, - const std::vector &B) { +std::vector +mergeC1AndC2Impl(const std::vector &A, + const std::vector &B) { // Build 2 mappings auto [OffsetMap, ModifiedRangeList] = buildMapping(A); std::string FilePath = A[0].getFilePath().str(); - std::vector Result; + std::vector Result; std::vector AGroupRepls; for (const auto &repl : ModifiedRangeList) { AGroupRepls.push_back( @@ -498,23 +523,24 @@ std::vector mergeC1AndC2Impl(const std::vector &A, auto IterA = AGroupRepls.begin(); auto IterB = BGroupRepls.begin(); - std::optional> + std::optional< + std::tuple> UnfinishedRepl = std::nullopt; while (IterA != AGroupRepls.end() || IterB != BGroupRepls.end()) { - bool NoA = IterA == AGroupRepls.end(); - bool NoB = IterB == BGroupRepls.end(); + const bool NoA = IterA == AGroupRepls.end(); + const bool NoB = IterB == BGroupRepls.end(); if (UnfinishedRepl) { - unsigned Off = std::get<0>(*UnfinishedRepl); - unsigned Len = std::get<1>(*UnfinishedRepl); - std::string Text = std::get<2>(*UnfinishedRepl); + const unsigned Off = std::get<0>(*UnfinishedRepl); + const unsigned Len = std::get<1>(*UnfinishedRepl); + const std::string Text = std::get<2>(*UnfinishedRepl); if ((NoA || (Off + Len) < IterA->getOffset()) && (NoB || (Off + Len) < IterB->getOffset())) { // Finished Result.push_back({FilePath, mapToOriginalOffset(Off, OffsetMap), mapToOriginalOffset(Off + Len, OffsetMap) - mapToOriginalOffset(Off, OffsetMap), - Text}); + Text, std::get<3>(*UnfinishedRepl)}); UnfinishedRepl = std::nullopt; continue; } @@ -526,6 +552,7 @@ std::vector mergeC1AndC2Impl(const std::vector &A, std::get<2>(*UnfinishedRepl) = Text.substr(0, IterB->getOffset() - Off) + IterB->getReplacementText().str(); + std::get<3>(*UnfinishedRepl) = true; ++IterB; continue; } @@ -553,24 +580,24 @@ std::vector mergeC1AndC2Impl(const std::vector &A, mapToOriginalOffset(IterB->getOffset() + IterB->getLength(), OffsetMap) - mapToOriginalOffset(IterB->getOffset(), OffsetMap), - IterB->getReplacementText()}); + IterB->getReplacementText(), true}); ++IterB; continue; } if (NoB) { // only left A group replacements - Result.push_back(*IterA); + Result.push_back({*IterA, false}); ++IterA; continue; } if (IterA->getOffset() < IterB->getOffset()) { - UnfinishedRepl = std::make_tuple(IterA->getOffset(), - IterA->getReplacementText().size(), - IterA->getReplacementText().str()); + UnfinishedRepl = std::make_tuple( + IterA->getOffset(), IterA->getReplacementText().size(), + IterA->getReplacementText().str(), false); ++IterA; } else { UnfinishedRepl = std::make_tuple(IterB->getOffset(), IterB->getLength(), - IterB->getReplacementText().str()); + IterB->getReplacementText().str(), true); ++IterB; } } @@ -583,7 +610,7 @@ std::vector mergeC1AndC2Impl(const std::vector &A, std::get<1>(*UnfinishedRepl), OffsetMap) - mapToOriginalOffset(std::get<0>(*UnfinishedRepl), OffsetMap), - std::get<2>(*UnfinishedRepl)}); + std::get<2>(*UnfinishedRepl), std::get<3>(*UnfinishedRepl)}); } std::sort(Result.begin(), Result.end()); @@ -592,11 +619,11 @@ std::vector mergeC1AndC2Impl(const std::vector &A, } // namespace // Merge Repl_C1 and Repl_C2. If has conflict, keep repl from Repl_C2. -std::vector mergeC1AndC2( +std::vector mergeC1AndC2( const std::vector &Repl_C1, const GitDiffChanges &Repl_C2, const std::map &FileNameMap) { - std::vector Result; + std::vector Result; std::vector Repl_C2_vec; std::for_each(Repl_C2.ModifyFileHunks.begin(), Repl_C2.ModifyFileHunks.end(), [&Repl_C2_vec, FileNameMap](const Replacement &Hunk) { @@ -614,7 +641,9 @@ std::vector mergeC1AndC2( auto ItC2 = C2.find(FilePath); if (ItC2 == C2.end()) { // No replacements in C2 for this file, just add C1 - Result.insert(Result.end(), Repls_C1.begin(), Repls_C1.end()); + for (const auto &R : Repls_C1) { + Result.push_back({R, false}); // false means not containing manual fix + } continue; } // Merge replacements in C1 and C2 for this file @@ -681,7 +710,8 @@ std::map> reMigrationMerge( // file4.dp.hpp. We need convert the filename in Repl_C2 to CUDA style // 2. Repl_C1 is based on CUDA code, Repl_C2 is based on the migrated SYCL // code, the result Repl_C should based on the original CUDA code. - std::vector Repl_C = mergeC1AndC2(Repl_C1, Repl_C2, FileNameMap); + std::vector Repl_C = + mergeC1AndC2(Repl_C1, Repl_C2, FileNameMap); // Convert vector in Repl_A to map for quick lookup. std::map> @@ -710,7 +740,7 @@ std::map> reMigrationMerge( } // Get Repl_C_y - std::map Repl_C_y; + std::map> Repl_C_y; for (const auto &Repl : Repl_C) { // The gitdiff changes are line-based while clang replacements are @@ -718,9 +748,7 @@ std::map> reMigrationMerge( // covered by delete hunk) between delete hunks and replacements. const auto &It = DeletedParts.find(Repl.getFilePath().str()); if (It == DeletedParts.end()) { - llvm::cantFail(Repl_C_y[Repl.getFilePath().str()].add( - Replacement(Repl.getFilePath().str(), Repl.getOffset(), - Repl.getLength(), Repl.getReplacementText()))); + Repl_C_y[Repl.getFilePath().str()].push_back(Repl); continue; } @@ -733,13 +761,11 @@ std::map> reMigrationMerge( break; } if (!HasConflict) - llvm::cantFail(Repl_C_y[Repl.getFilePath().str()].add( - Replacement(Repl.getFilePath().str(), Repl.getOffset(), - Repl.getLength(), Repl.getReplacementText()))); + Repl_C_y[Repl.getFilePath().str()].push_back(Repl); } // Shift Repl_C_y with Repl_A(ModifiedParts) - std::map Repl_D; + std::map> Repl_D; for (const auto &Item : Repl_C_y) { const auto &FilePath = Item.first; const auto &Repls = Item.second; @@ -762,7 +788,7 @@ std::map> reMigrationMerge( convertReplcementsLineString(Pair.second); if (Repl_D.find(Pair.first) != Repl_D.end()) { // Merge Repl_D and Repl_B - std::map ReplDInLines = + std::map> ReplDInLines = convertReplcementsLineString(Repl_D[Pair.first]); Result[Pair.first] = mergeMapsByLine(ReplBInLines, ReplDInLines, Pair.first); diff --git a/clang/lib/DPCT/IncMigration/ReMigration.h b/clang/lib/DPCT/IncMigration/ReMigration.h index 4d64498e4e66..0dcef580e35e 100644 --- a/clang/lib/DPCT/IncMigration/ReMigration.h +++ b/clang/lib/DPCT/IncMigration/ReMigration.h @@ -96,20 +96,37 @@ GitDiffChanges &getUpstreamChanges(); GitDiffChanges &getUserChanges(); clang::tooling::TranslationUnitReplacements &getLastMigration(); -clang::tooling::Replacements +class TaggedReplacement : public clang::tooling::Replacement { +public: + TaggedReplacement() = default; + TaggedReplacement(const clang::tooling::Replacement &R, + bool ContainsManualFix) + : clang::tooling::Replacement(R), ContainsManualFix(ContainsManualFix) {} + + TaggedReplacement(StringRef FilePath, unsigned Offset, unsigned Length, + StringRef ReplacementText, bool ContainsManualFix) + : TaggedReplacement(clang::tooling::Replacement(FilePath, Offset, Length, + ReplacementText), + ContainsManualFix) {} + bool containsManualFix() const { return ContainsManualFix; } + +private: + bool ContainsManualFix = true; +}; +std::vector calculateUpdatedRanges(const clang::tooling::Replacements &Repls, - const clang::tooling::Replacements &NewRepl); + const std::vector &NewRepl); std::map> groupReplcementsByFile(const std::vector &Repls); -std::vector splitReplInOrderToNotCrossLines( - const std::vector &InRepls); -std::map -convertReplcementsLineString(const std::vector &Repls); +std::vector +splitReplInOrderToNotCrossLines(const std::vector &InRepls); +std::map> +convertReplcementsLineString(const std::vector &Repls); std::vector mergeMapsByLine(const std::map &MapA, - const std::map &MapB, + const std::map> &MapB, const clang::tooling::UnifiedPath &FilePath); -std::vector +std::vector mergeC1AndC2(const std::vector &Repl_C1, const GitDiffChanges &Repl_C2, const std::map #include #include #include -======= -#include -#include ->>>>>>> <<<<<<< /* diff --git a/clang/unittests/DPCT/ReMigrationTest.cpp b/clang/unittests/DPCT/ReMigrationTest.cpp index 683e2d820c99..8e727c6f82f3 100644 --- a/clang/unittests/DPCT/ReMigrationTest.cpp +++ b/clang/unittests/DPCT/ReMigrationTest.cpp @@ -48,15 +48,15 @@ aaa bb ccc */ // clang-format on - Replacements NewRepl; + std::vector NewRepl; Replacements Repls; Replacements Expected; - llvm::cantFail(NewRepl.add(Replacement("file1.cpp", 4, 2, "zzz"))); - llvm::cantFail(NewRepl.add(Replacement("file1.cpp", 11, 0, "ppp "))); - llvm::cantFail(NewRepl.add(Replacement("file1.cpp", 26, 2, "yyy"))); - llvm::cantFail(NewRepl.add(Replacement("file1.cpp", 46, 1, "xxx"))); - llvm::cantFail(NewRepl.add(Replacement("file1.cpp", 65, 1, "\nqqq"))); + NewRepl.push_back({"file1.cpp", 4, 2, "zzz", true}); + NewRepl.push_back({"file1.cpp", 11, 0, "ppp ", true}); + NewRepl.push_back({"file1.cpp", 26, 2, "yyy", true}); + NewRepl.push_back({"file1.cpp", 46, 1, "xxx", true}); + NewRepl.push_back({"file1.cpp", 65, 1, "\nqqq", true}); llvm::cantFail( Repls.add(Replacement("file1.cpp", 11, 11, "Hi bb Everyone\n"))); @@ -70,7 +70,8 @@ aaa bb ccc llvm::cantFail(Expected.add(Replacement("file1.cpp", 4, 2, "zzz"))); llvm::cantFail(Expected.add(Replacement("file1.cpp", 30, 2, "yyy"))); - Replacements Result = calculateUpdatedRanges(Repls, NewRepl); + std::vector Result = + calculateUpdatedRanges(Repls, NewRepl); ASSERT_EQ(Expected.size(), Result.size()); size_t Num = Expected.size(); auto ExpectedIt = Expected.begin(); @@ -193,15 +194,21 @@ TEST_F(ReMigrationTest2, splitReplInOrderToNotCrossLines) { getLineStringHook = this->getLineStringUnittest; getLineNumberHook = this->getLineNumberUnittest; getLineBeginOffsetHook = this->getLineBeginOffsetUnittest; - std::vector Repls = {Replacement("file1.cpp", 7, 18, "jjj\nkkk"), - Replacement("file1.cpp", 41, 5, "xx")}; + std::vector Repls = { + {"file1.cpp", 7, 18, "jjj\nkkk", true}, {"file1.cpp", 41, 5, "xx", true}}; std::vector Expected = { Replacement("file1.cpp", 7, 4, "jjj\nkkkhhhhiii\n"), Replacement("file1.cpp", 11, 11, ""), Replacement("file1.cpp", 22, 11, ""), Replacement("file1.cpp", 41, 3, "xxyyyyyyyy\n"), Replacement("file1.cpp", 44, 11, "")}; - auto Result = splitReplInOrderToNotCrossLines(Repls); + auto Temp = splitReplInOrderToNotCrossLines(Repls); + std::vector Result; + // drop the tag info + for (const auto &R : Temp) { + Result.push_back({R.getFilePath(), R.getOffset(), R.getLength(), + R.getReplacementText()}); + } std::sort(Result.begin(), Result.end()); EXPECT_EQ(Expected, Result); } @@ -268,12 +275,12 @@ eee bb ccc getLineStringHook = this->getLineStringUnittest; getLineNumberHook = this->getLineNumberUnittest; getLineBeginOffsetHook = this->getLineBeginOffsetUnittest; - std::vector Repls = { - Replacement("file1.cpp", 4, 2, "zzz"), - Replacement("file1.cpp", 64, 3, "q\nqqq"), - Replacement("file1.cpp", 33, 11, "aaa yyy ccc\n"), - Replacement("file1.cpp", 11, 0, "ppp "), - Replacement("file1.cpp", 84, 18, "ddd\neee")}; + std::vector Repls = { + {"file1.cpp", 4, 2, "zzz", true}, + {"file1.cpp", 64, 3, "q\nqqq", true}, + {"file1.cpp", 33, 11, "aaa yyy ccc\n", true}, + {"file1.cpp", 11, 0, "ppp ", true}, + {"file1.cpp", 84, 18, "ddd\neee", true}}; std::map Expected = {{1, "aaa zzz ccc\n"}, {2, "ppp aaa bb ccc\n"}, {4, "aaa yyy ccc\n"}, @@ -282,7 +289,12 @@ eee bb ccc {8, "aaa bb ddd\neee bb ccc\n"}, {9, ""}, {10, ""}}; - auto Result = convertReplcementsLineString(Repls); + auto Temp = convertReplcementsLineString(Repls); + std::map Result; + // drop the tag info + for (const auto &R : Temp) { + Result[R.first] = R.second.first; + } EXPECT_EQ(Expected, Result); } @@ -292,8 +304,12 @@ TEST_F(ReMigrationTest3, mergeMapsByLine) { std::map MapA = {{1, "zzzzz\n"}, {3, "xxxx1\n"}, {4, "wwww1\n"}, {5, "vvvvv\n"}, {7, "ppppp\n"}, {9, "iiijjjkkk\n"}}; - std::map MapB = { - {2, "yyyyy\n"}, {3, "xxxx2\n"}, {4, "wwww2\n"}, {7, ""}, {8, "qqqqq"}}; + std::map> MapB = { + {2, {"yyyyy\n", true}}, + {3, {"xxxx2\n", true}}, + {4, {"wwww2\n", true}}, + {7, {"", true}}, + {8, {"qqqqq", true}}}; UnifiedPath FilePath("test.cu"); auto Result = mergeMapsByLine(MapA, MapB, FilePath); std::sort(Result.begin(), Result.end()); @@ -362,242 +378,13 @@ bbyyy2345678xxxc78www0123456789 const std::map FileNameMap = { {"file1.dp.cpp", "file1.cu"}}; - std::vector Result = mergeC1AndC2(Repl_C1, Repl_C2, FileNameMap); - std::vector Expected = {Replacement("file1.cu", 4, 0, "zzz"), - Replacement("file1.cu", 10, 0, "aaa"), - Replacement("file1.cu", 11, 2, "bbyyy"), - Replacement("file1.cu", 20, 20, "xxxc"), - Replacement("file1.cu", 42, 2, "www")}; - std::sort(Result.begin(), Result.end()); - std::sort(Expected.begin(), Expected.end()); - - ASSERT_EQ(Expected.size(), Result.size()); - size_t Num = Expected.size(); - auto ExpectedIt = Expected.begin(); - auto ResultIt = Result.begin(); - for (size_t i = 0; i < Num; ++i) { - EXPECT_EQ(ExpectedIt->getFilePath(), ResultIt->getFilePath()); - EXPECT_EQ(ExpectedIt->getOffset(), ResultIt->getOffset()); - EXPECT_EQ(ExpectedIt->getLength(), ResultIt->getLength()); - EXPECT_EQ(ExpectedIt->getReplacementText(), ResultIt->getReplacementText()); - ExpectedIt++; - ResultIt++; - } -} - -class ReMigrationTest4 : public ::testing::Test { -protected: - inline static std::vector CUDACodeV2Vec = {}; - inline static std::vector LineOffsets = {}; - void SetUp() override { - // clang-format off - const std::string CUDACodeV2 = R"(#include - -#define CUDA_CHECK(call) \ - do { \ - cudaError_t err = call; \ - if (err != cudaSuccess) { \ - printf("CUDA error in %s at line %d: %s\n", __FILE__, __LINE__, \ - cudaGetErrorString(err)); \ - exit(EXIT_FAILURE); \ - } \ - } while (0) - -void foo() { - float *g; - CUDA_CHECK(cudaMalloc(&g, 100 * sizeof(float))); - float *h; - CUDA_CHECK(cudaMalloc(&h, 100 * sizeof(float))); - cudaDeviceSynchronize(); - cudaFree(g); - cudaFree(h); -} -)"; - // clang-format on - std::istringstream ISS(CUDACodeV2); - std::string Line; - // TODO: This code only considers the last line is empty (file ending by a \n). - // What if the last line is not empty? - while (true) { - bool LastLine = false; - if (!std::getline(ISS, Line)) - LastLine = true; - else - Line += '\n'; - LineOffsets.push_back(LineOffsets.empty() - ? 0 - : LineOffsets.back() + - CUDACodeV2Vec.back().size()); - CUDACodeV2Vec.push_back(Line); - if (LastLine) - break; - } - LineOffsets.insert(LineOffsets.begin(), 0); - } - void TearDown() override { CUDACodeV2Vec.clear(); } - static StringRef getLineStringUnittest(clang::tooling::UnifiedPath FilePath, - unsigned LineNumber) { - return StringRef(CUDACodeV2Vec[LineNumber - 1]); - } - static unsigned getLineNumberUnittest(clang::tooling::UnifiedPath FilePath, - unsigned Offset) { - auto Iter = - std::upper_bound(LineOffsets.begin() + 1, LineOffsets.end(), Offset); - if (Iter == LineOffsets.end()) - return LineOffsets.size(); - return std::distance(LineOffsets.begin() + 1, Iter); - } - static unsigned - getLineBeginOffsetUnittest(clang::tooling::UnifiedPath FilePath, - unsigned LineNumber) { - return LineOffsets[LineNumber]; - } -}; - -TEST_F(ReMigrationTest4, reMigrationMerge) { - getLineStringHook = this->getLineStringUnittest; - getLineNumberHook = this->getLineNumberUnittest; - getLineBeginOffsetHook = this->getLineBeginOffsetUnittest; - - const std::map FileNameMap = { - {"test.dp.cpp", "test.cu"}}; - - std::vector Repl_C1 = { - Replacement("test.cu", 0, 0, - "#include \n#include \n"), - Replacement( - "test.cu", 20, 0, - "/*\nDPCT1009:0: SYCL reports errors using exceptions and does not " - "use error codes. Please replace the \"get_error_string_dummy(...)\" " - "with a real error-handling function.\n*/\n"), - Replacement("test.cu", 186, 11, "dpct::err0"), - Replacement("test.cu", 267, 325, ""), - Replacement("test.cu", 694, 0, " try "), - Replacement( - "test.cu", 695, 0, - "\n dpct::device_ext &dev_ct1 = dpct::get_current_device();\n " - "sycl::queue &q_ct1 = dev_ct1.in_order_queue();"), - Replacement( - "test.cu", 721, 35, - "DPCT_CHECK_ERROR(f = sycl::malloc_device(100, q_ct1))"), - Replacement( - "test.cu", 784, 35, - "DPCT_CHECK_ERROR(g = sycl::malloc_device(100, q_ct1))"), - Replacement("test.cu", 824, 63, - "q_ct1.memcpy(f, g, 100 * sizeof(float))"), - Replacement("test.cu", 891, 23, "dev_ct1.queues_wait_and_throw()"), - Replacement("test.cu", 918, 11, "dpct::dpct_free(f, q_ct1)"), - Replacement("test.cu", 933, 11, "dpct::dpct_free(g, q_ct1)"), - Replacement("test.cu", 947, 0, - "\ncatch (sycl::exception const &exc) {\n std::cerr << " - "exc.what() << \"Exception caught at file:\" << __FILE__ << " - "\", line:\" << __LINE__ << std::endl;\n std::exit(1);\n}")}; - - GitDiffChanges Repl_C2; - Repl_C2.ModifyFileHunks = { - Replacement("test.dp.cpp", 70, 526, "void foo() {\n"), - Replacement("test.dp.cpp", 715, 76, - " f = sycl::malloc_device(100, q_ct1);\n"), - Replacement("test.dp.cpp", 803, 76, - " g = sycl::malloc_device(100, q_ct1);\n"), - Replacement("test.dp.cpp", 1017, 163, "")}; - - GitDiffChanges Repl_A; - Repl_A.ModifyFileHunks = { - Replacement("test.cu", 696, 63, ""), - Replacement( - "test.cu", 822, 67, - " float *h;\n CUDA_CHECK(cudaMalloc(&h, 100 * sizeof(float)));\n"), - Replacement("test.cu", 916, 15, ""), - Replacement("test.cu", 946, 0, " cudaFree(h);\n")}; - - std::vector Repl_B = { - Replacement("test.cu", 0, 0, - "#include \n#include \n"), - Replacement( - "test.cu", 20, 0, - "/*\nDPCT1009:0: SYCL reports errors using exceptions and does not " - "use error codes. Please replace the \"get_error_string_dummy(...)\" " - "with a real error-handling function.\n*/\n"), - Replacement("test.cu", 186, 11, "dpct::err0"), - Replacement("test.cu", 267, 325, ""), - Replacement("test.cu", 694, 0, " try "), - Replacement( - "test.cu", 695, 0, - "\n dpct::device_ext &dev_ct1 = dpct::get_current_device();\n " - "sycl::queue &q_ct1 = dev_ct1.in_order_queue();"), - Replacement( - "test.cu", 721, 35, - "DPCT_CHECK_ERROR(g = sycl::malloc_device(100, q_ct1))"), - Replacement( - "test.cu", 784, 35, - "DPCT_CHECK_ERROR(h = sycl::malloc_device(100, q_ct1))"), - Replacement("test.cu", 824, 23, "dev_ct1.queues_wait_and_throw()"), - Replacement("test.cu", 851, 11, "dpct::dpct_free(g, q_ct1)"), - Replacement("test.cu", 866, 11, "dpct::dpct_free(h, q_ct1)"), - Replacement("test.cu", 880, 0, - "\ncatch (sycl::exception const &exc) {\n std::cerr << " - "exc.what() << \"Exception caught at file:\" << __FILE__ << " - "\", line:\" << __LINE__ << std::endl;\n std::exit(1);\n}")}; - - std::map> ResultMap = - reMigrationMerge(Repl_A, Repl_B, Repl_C1, Repl_C2, FileNameMap); - - std::vector Result = ResultMap.begin()->second; - std::vector Expected = { - Replacement("test.cu", 0, 19, R"(#include -#include -#include -)"), - Replacement("test.cu", 20, 81, R"xxx(<<<<<<< -/* -DPCT1009:0: SYCL reports errors using exceptions and does not use error codes. Please replace the "get_error_string_dummy(...)" with a real error-handling function. -*/ -#define CUDA_CHECK(call) \ -======= -void foo() { - dpct::device_ext &dev_ct1 = dpct::get_current_device(); - sycl::queue &q_ct1 = dev_ct1.in_order_queue(); ->>>>>>> -)xxx"), - Replacement("test.cu", 101, 81, R"()"), - Replacement("test.cu", 182, 486, R"(<<<<<<< - dpct::err0 err = call; \ - \ -======= ->>>>>>> -)"), - Replacement("test.cu", 668, 14, R"()"), - Replacement("test.cu", 682, 1, R"()"), - Replacement("test.cu", 683, 13, R"(<<<<<<< -void foo() try { - dpct::device_ext &dev_ct1 = dpct::get_current_device(); - sycl::queue &q_ct1 = dev_ct1.in_order_queue(); -======= ->>>>>>> -)"), - Replacement("test.cu", 708, 51, R"(<<<<<<< - CUDA_CHECK(DPCT_CHECK_ERROR(g = sycl::malloc_device(100, q_ct1))); -======= - g = sycl::malloc_device(100, q_ct1); ->>>>>>> -)"), - Replacement( - "test.cu", 771, 51, - R"( CUDA_CHECK(DPCT_CHECK_ERROR(h = sycl::malloc_device(100, q_ct1))); -)"), - Replacement("test.cu", 822, 27, R"( dev_ct1.queues_wait_and_throw(); -)"), - Replacement("test.cu", 849, 15, R"( dpct::dpct_free(g, q_ct1); -)"), - Replacement("test.cu", 864, 15, R"( dpct::dpct_free(h, q_ct1); -)"), - Replacement("test.cu", 879, 2, R"(} -catch (sycl::exception const &exc) { - std::cerr << exc.what() << "Exception caught at file:" << __FILE__ << ", line:" << __LINE__ << std::endl; - std::exit(1); -} -)")}; + std::vector Result = + mergeC1AndC2(Repl_C1, Repl_C2, FileNameMap); + std::vector Expected = {{"file1.cu", 4, 0, "zzz", true}, + {"file1.cu", 10, 0, "aaa", false}, + {"file1.cu", 11, 2, "bbyyy", true}, + {"file1.cu", 20, 20, "xxxc", true}, + {"file1.cu", 42, 2, "www", true}}; std::sort(Result.begin(), Result.end()); std::sort(Expected.begin(), Expected.end()); @@ -610,6 +397,7 @@ catch (sycl::exception const &exc) { EXPECT_EQ(ExpectedIt->getOffset(), ResultIt->getOffset()); EXPECT_EQ(ExpectedIt->getLength(), ResultIt->getLength()); EXPECT_EQ(ExpectedIt->getReplacementText(), ResultIt->getReplacementText()); + EXPECT_EQ(ExpectedIt->containsManualFix(), ResultIt->containsManualFix()); ExpectedIt++; ResultIt++; } From 1d76652897131dc101bed5274207f25727774be0 Mon Sep 17 00:00:00 2001 From: "Jiang, Zhiwei" Date: Fri, 4 Jul 2025 16:43:57 +0800 Subject: [PATCH 45/53] Update Signed-off-by: Jiang, Zhiwei --- clang/lib/DPCT/DPCT.cpp | 2 +- clang/lib/DPCT/FileGenerator/GenFiles.cpp | 2 +- clang/test/dpct/remigration/LastMigration.yaml | 4 ++-- clang/test/dpct/remigration/test.cpp | 4 +++- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/clang/lib/DPCT/DPCT.cpp b/clang/lib/DPCT/DPCT.cpp index 54de5198bd1b..43ad411e394f 100644 --- a/clang/lib/DPCT/DPCT.cpp +++ b/clang/lib/DPCT/DPCT.cpp @@ -808,6 +808,7 @@ int runDPCT(int argc, const char **argv) { CudaIncludePath = CudaInclude; SDKPath = SDKPathOpt; + DpctGlobalInfo::setReMigration(ReMigration); loadMainSrcFileInfo(OutRootPath.getCanonicalPath() + "/MainSourceFiles.yaml"); std::transform( @@ -1240,7 +1241,6 @@ int runDPCT(int argc, const char **argv) { DpctGlobalInfo::setOptimizeMigrationFlag(OptimizeMigration.getValue()); DpctGlobalInfo::setSYCLFileExtension(SYCLFileExtension); DpctGlobalInfo::setUseSYCLCompat(UseSYCLCompat); - DpctGlobalInfo::setReMigration(ReMigration); StopOnParseErrTooling = StopOnParseErr; InRootTooling = InRootPath; diff --git a/clang/lib/DPCT/FileGenerator/GenFiles.cpp b/clang/lib/DPCT/FileGenerator/GenFiles.cpp index db6eb09f8a1b..77fb3186c808 100644 --- a/clang/lib/DPCT/FileGenerator/GenFiles.cpp +++ b/clang/lib/DPCT/FileGenerator/GenFiles.cpp @@ -539,7 +539,7 @@ int writeReplacementsToFiles( } } - for (const auto &Repl : clang::dpct::getLastMigration().Replacements) { + for (const auto &Repl : clang::dpct::getLastMigration()->Replacements) { Repl_C1.push_back(Repl); } std::map> Result = diff --git a/clang/test/dpct/remigration/LastMigration.yaml b/clang/test/dpct/remigration/LastMigration.yaml index adc597226381..b3db45c35693 100644 --- a/clang/test/dpct/remigration/LastMigration.yaml +++ b/clang/test/dpct/remigration/LastMigration.yaml @@ -290,7 +290,7 @@ Replacements: NewHostVarName: '' BlockLevelFormatFlag: false MainSourceFilesDigest: - - MainSourceFile: '/home/zhiwei/testfolder/test/test.cu' + - MainSourceFile: 'PATH_PLACEHOLDER/test.cu' Digest: 63232f272cda678b14a86a4243a28f63 HasCUDASyntax: true DpctVersion: 21.0.0 @@ -304,7 +304,7 @@ OptionMap: AnalysisScopePath: Value: '' ValueVec: - - '/home/zhiwei/testfolder/test' + - 'PATH_PLACEHOLDER' Specified: false AsyncHandler: Value: 'false' diff --git a/clang/test/dpct/remigration/test.cpp b/clang/test/dpct/remigration/test.cpp index 9f868830311b..772ce93bdab1 100644 --- a/clang/test/dpct/remigration/test.cpp +++ b/clang/test/dpct/remigration/test.cpp @@ -1,7 +1,9 @@ // UNSUPPORTED: system-windows // RUN: cd %T -// RUN: cp %S/LastMigration.yaml . +// RUN: mkdir out +// RUN: cp %S/LastMigration.yaml out/MainSourceFiles.yaml +// RUN: sed -i "s|PATH_PLACEHOLDER|$(pwd)|g" out/MainSourceFiles.yaml // RUN: cp %S/UpstreamChanges.yaml . // RUN: cp %S/UserChanges.yaml . // RUN: cp %S/src.txt test.cu From 88db20d55b3b365d8d337c9c4ec022b76430d555 Mon Sep 17 00:00:00 2001 From: "Jiang, Zhiwei" Date: Mon, 7 Jul 2025 15:32:30 +0800 Subject: [PATCH 46/53] Update Signed-off-by: Jiang, Zhiwei --- clang/CMakeLists.txt | 1 + clang/lib/DPCT/IncMigration/ReMigration.cpp | 37 +- clang/test/CMakeLists.txt | 1 + .../dpct/remigration/UpstreamChanges.yaml | 8 +- clang/test/dpct/remigration/UserChanges.yaml | 14 +- clang/tools/CMakeLists.txt | 1 + clang/tools/{dpct => }/gitdiff2yaml/1.ref.txt | 0 clang/tools/{dpct => }/gitdiff2yaml/2.ref.txt | 0 clang/tools/{dpct => }/gitdiff2yaml/3.ref.txt | 0 clang/tools/{dpct => }/gitdiff2yaml/4.ref.txt | 0 clang/tools/gitdiff2yaml/CMakeLists.txt | 14 + .../src}/gitdiff2yaml.cpp | 398 +++++++++--------- clang/tools/gitdiff2yaml/src/gitdiff2yaml.h | 38 ++ clang/tools/gitdiff2yaml/src/main.cpp | 103 +++++ clang/tools/{dpct => }/gitdiff2yaml/test.py | 0 clang/tools/{dpct => }/gitdiff2yaml/tests.zip | Bin 16 files changed, 387 insertions(+), 228 deletions(-) rename clang/tools/{dpct => }/gitdiff2yaml/1.ref.txt (100%) rename clang/tools/{dpct => }/gitdiff2yaml/2.ref.txt (100%) rename clang/tools/{dpct => }/gitdiff2yaml/3.ref.txt (100%) rename clang/tools/{dpct => }/gitdiff2yaml/4.ref.txt (100%) create mode 100644 clang/tools/gitdiff2yaml/CMakeLists.txt rename clang/tools/{dpct/gitdiff2yaml => gitdiff2yaml/src}/gitdiff2yaml.cpp (64%) create mode 100644 clang/tools/gitdiff2yaml/src/gitdiff2yaml.h create mode 100644 clang/tools/gitdiff2yaml/src/main.cpp rename clang/tools/{dpct => }/gitdiff2yaml/test.py (100%) rename clang/tools/{dpct => }/gitdiff2yaml/tests.zip (100%) diff --git a/clang/CMakeLists.txt b/clang/CMakeLists.txt index b8860f4cc045..77922a85b82b 100644 --- a/clang/CMakeLists.txt +++ b/clang/CMakeLists.txt @@ -908,6 +908,7 @@ if (NOT CLANG_BUILT_STANDALONE) list(APPEND DPCT_RUN install-dpct-intercept-build install-dpct-pattern-rewriter + install-dpct-gitdiff2yaml install-dpct-headers install-clang-resource-headers install-dpct-binary diff --git a/clang/lib/DPCT/IncMigration/ReMigration.cpp b/clang/lib/DPCT/IncMigration/ReMigration.cpp index dc521d4b0a4a..8f11051fae09 100644 --- a/clang/lib/DPCT/IncMigration/ReMigration.cpp +++ b/clang/lib/DPCT/IncMigration/ReMigration.cpp @@ -6,24 +6,25 @@ // //===----------------------------------------------------------------------===// // Workflow: -// CUDA code v1 -// | -// | dpct (--format-range=off) => MainSourceFiles.yaml and *.h.yaml -// v -// SYCL code v1 -// | 1. git init and commit -// | 2. manual format with clang-format -// | 3. manual fix -// v 4. gitdiff2yaml => UserChange.yaml -// SYCL code v1.1 -// -// CUDA code v2 -// | -// | dpct (--format-range=off) -// | + MainSourceFiles.yaml and *.h.yaml -// | + UserChange.yaml -// v -// SYCL code v2 +// CUDA code v1 +// | +// | dpct (--format-range=off) => MainSourceFiles.yaml +// v +// SYCL code v1 +// | 1. git init and commit +// | 2. manual format with clang-format +// | 3. manual fix +// v 4. gitdiff2yaml => UserChange.yaml +// SYCL code v1.1 +// +// CUDA code v2 +// | +// | dpct (--format-range=off) +// | and MainSourceFiles.yaml +// | and UserChange.yaml +// | and UpStreamChange.yaml +// v +// SYCL code v2 //===----------------------------------------------------------------------===// #include "ReMigration.h" diff --git a/clang/test/CMakeLists.txt b/clang/test/CMakeLists.txt index acc325a95685..391c8069c006 100644 --- a/clang/test/CMakeLists.txt +++ b/clang/test/CMakeLists.txt @@ -89,6 +89,7 @@ list(APPEND CLANG_TEST_DEPS dpct-codepin-report-files scan-build-py pattern-rewriter-binary + gitdiff2yaml-binary ) if(CLANG_ENABLE_STATIC_ANALYZER) diff --git a/clang/test/dpct/remigration/UpstreamChanges.yaml b/clang/test/dpct/remigration/UpstreamChanges.yaml index d3cde8719110..842c6b22accb 100644 --- a/clang/test/dpct/remigration/UpstreamChanges.yaml +++ b/clang/test/dpct/remigration/UpstreamChanges.yaml @@ -1,18 +1,18 @@ --- ModifyFileHunks: - - FilePath: 'test.cu' + - FilePath: test.cu Offset: 26 Length: 0 ReplacementText: "#include \"cublas_v2.h\"\n" - - FilePath: 'test.cu' + - FilePath: test.cu Offset: 708 Length: 0 ReplacementText: "#define CHECK_CUBLAS(call) \\\n do { \\\n cublasStatus_t err = call; \\\n if (err != CUBLAS_STATUS_SUCCESS) { \\\n fprintf(stderr, \"CUBLAS error in %s at line %d: %d\\n\", __FILE__, \\\n __LINE__, err); \\\n exit(EXIT_FAILURE); \\\n } \\\n } while (0)\n\n" - - FilePath: 'test.cu' + - FilePath: test.cu Offset: 869 Length: 0 ReplacementText: "void scale_cublas(float *data, float scale, float *output, int size) {\n cublasHandle_t handle;\n CHECK_CUBLAS(cublasCreate(&handle));\n float alpha = scale;\n CHECK_CUBLAS(cublasSscal(handle, size, &alpha, data, 1));\n CHECK_CUDA(cudaDeviceSynchronize());\n CHECK_CUBLAS(cublasDestroy(handle));\n}\n\ntemplate\n" - - FilePath: 'test.cu' + - FilePath: test.cu Offset: 1174 Length: 404 ReplacementText: " if (use_cublas) {\n scale_cublas(input_d, scale, output_d, size);\n } else {\n int blockSize = 256;\n int numBlocks = (size + blockSize - 1) / blockSize;\n scale_kernel<<>>(input_d, scale, output_d);\n CHECK_CUDA(cudaDeviceSynchronize());\n cudaError_t err = cudaGetLastError();\n if (err != cudaSuccess) {\n fprintf(stderr, \"CUDA kernel launch failed: %s\\n\",\n cudaGetErrorString(err));\n cudaFree(input_d);\n cudaFree(output_d);\n exit(EXIT_FAILURE);\n }\n" diff --git a/clang/test/dpct/remigration/UserChanges.yaml b/clang/test/dpct/remigration/UserChanges.yaml index dc8297aa27c5..b5adf495d2f6 100644 --- a/clang/test/dpct/remigration/UserChanges.yaml +++ b/clang/test/dpct/remigration/UserChanges.yaml @@ -1,26 +1,26 @@ --- ModifyFileHunks: - - FilePath: 'test.dp.cpp' + - FilePath: test.dp.cpp Offset: 69 Length: 508 - ReplacementText: "" - - FilePath: 'test.dp.cpp' + ReplacementText: '' + - FilePath: test.dp.cpp Offset: 709 Length: 92 ReplacementText: " int idx = item_ct1.get_group(2) * item_ct1.get_local_range(2) +\n item_ct1.get_local_id(2);\n" - - FilePath: 'test.dp.cpp' + - FilePath: test.dp.cpp Offset: 839 Length: 69 ReplacementText: "void scale(float *data, float scale, float *output, int size) try {\n" - - FilePath: 'test.dp.cpp' + - FilePath: test.dp.cpp Offset: 1044 Length: 264 ReplacementText: " input_d = sycl::malloc_device(size, q_ct1);\n output_d = sycl::malloc_device(size, q_ct1);\n q_ct1.memcpy(input_d, data, size * sizeof(float)).wait();\n" - - FilePath: 'test.dp.cpp' + - FilePath: test.dp.cpp Offset: 1386 Length: 1252 ReplacementText: " q_ct1.parallel_for(sycl::nd_range<3>(sycl::range<3>(1, 1, numBlocks) *\n sycl::range<3>(1, 1, blockSize),\n sycl::range<3>(1, 1, blockSize)),\n [=](sycl::nd_item<3> item_ct1) {\n scale_kernel(input_d, scale, output_d);\n });\n dev_ct1.queues_wait_and_throw();\n" - - FilePath: 'test.dp.cpp' + - FilePath: test.dp.cpp Offset: 2639 Length: 371 ReplacementText: " q_ct1.memcpy(output, output_d, size * sizeof(float)).wait();\n dpct::dpct_free(input_d, q_ct1);\n dpct::dpct_free(output_d, q_ct1);\n} catch (sycl::exception const &exc) {\n std::cerr << exc.what() << \"Exception caught at file:\" << __FILE__\n << \", line:\" << __LINE__ << std::endl;\n" diff --git a/clang/tools/CMakeLists.txt b/clang/tools/CMakeLists.txt index c7d3cb52a892..4ca85f0d0b0a 100644 --- a/clang/tools/CMakeLists.txt +++ b/clang/tools/CMakeLists.txt @@ -52,6 +52,7 @@ add_clang_subdirectory(libclang) add_clang_subdirectory(dpct) add_clang_subdirectory(pattern-rewriter) +add_clang_subdirectory(gitdiff2yaml) add_clang_subdirectory(scan-build-py) add_clang_subdirectory(amdgpu-arch) add_clang_subdirectory(nvptx-arch) diff --git a/clang/tools/dpct/gitdiff2yaml/1.ref.txt b/clang/tools/gitdiff2yaml/1.ref.txt similarity index 100% rename from clang/tools/dpct/gitdiff2yaml/1.ref.txt rename to clang/tools/gitdiff2yaml/1.ref.txt diff --git a/clang/tools/dpct/gitdiff2yaml/2.ref.txt b/clang/tools/gitdiff2yaml/2.ref.txt similarity index 100% rename from clang/tools/dpct/gitdiff2yaml/2.ref.txt rename to clang/tools/gitdiff2yaml/2.ref.txt diff --git a/clang/tools/dpct/gitdiff2yaml/3.ref.txt b/clang/tools/gitdiff2yaml/3.ref.txt similarity index 100% rename from clang/tools/dpct/gitdiff2yaml/3.ref.txt rename to clang/tools/gitdiff2yaml/3.ref.txt diff --git a/clang/tools/dpct/gitdiff2yaml/4.ref.txt b/clang/tools/gitdiff2yaml/4.ref.txt similarity index 100% rename from clang/tools/dpct/gitdiff2yaml/4.ref.txt rename to clang/tools/gitdiff2yaml/4.ref.txt diff --git a/clang/tools/gitdiff2yaml/CMakeLists.txt b/clang/tools/gitdiff2yaml/CMakeLists.txt new file mode 100644 index 000000000000..2509b41bb3ca --- /dev/null +++ b/clang/tools/gitdiff2yaml/CMakeLists.txt @@ -0,0 +1,14 @@ +set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ") +set(LLVM_LINK_COMPONENTS + support +) + +add_clang_tool(gitdiff2yaml-binary + src/main.cpp + src/gitdiff2yaml.cpp +) +set_target_properties(gitdiff2yaml-binary PROPERTIES OUTPUT_NAME "gitdiff2yaml") +add_llvm_install_targets("install-dpct-gitdiff2yaml" + DEPENDS gitdiff2yaml-binary + COMPONENT gitdiff2yaml-binary +) diff --git a/clang/tools/dpct/gitdiff2yaml/gitdiff2yaml.cpp b/clang/tools/gitdiff2yaml/src/gitdiff2yaml.cpp similarity index 64% rename from clang/tools/dpct/gitdiff2yaml/gitdiff2yaml.cpp rename to clang/tools/gitdiff2yaml/src/gitdiff2yaml.cpp index 4d0c6ca9a8ee..c0ec70ff73e9 100644 --- a/clang/tools/dpct/gitdiff2yaml/gitdiff2yaml.cpp +++ b/clang/tools/gitdiff2yaml/src/gitdiff2yaml.cpp @@ -5,18 +5,12 @@ // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// -// Usage: -// $ g++ gitdiff2yaml.cpp -o gitdiff2yaml -// $ cd /path/to/your/git/repo -// $ ./gitdiff2yaml -// This will output the clang replacements in YAML format. -// Limitation: -// (1) The workspace and the staging area should be clean before running -// this tool. -// (2) The line ending in the file should be '\n'. -//===----------------------------------------------------------------------===// -#include +#include "gitdiff2yaml.h" + +#include "llvm/Support/YAMLTraits.h" +#include "llvm/Support/raw_os_ostream.h" + #include #include #include @@ -30,39 +24,6 @@ #include namespace { - -const std::string LineEnd = "\\n"; - -std::string execGitCommand(const std::string &CMD) { - std::array Buffer; - std::unique_ptr Pipe(popen(CMD.c_str(), "r"), pclose); - if (!Pipe) { - throw std::runtime_error("popen() failed!"); - } - - std::string Result; - while (fgets(Buffer.data(), Buffer.size(), Pipe.get()) != nullptr) { - Result += Buffer.data(); - } - return Result; -} - -struct Replacement { - std::string NewFilePath; - std::string OldFilePath; - unsigned Offset = 0; - unsigned Length = 0; - std::string ReplacementText; -}; - -struct HunkContext { - unsigned OldCurrentLine = 0; - bool InHunk = false; - bool FastForward = false; - std::string CurrentNewFilePath; - std::string CurrentOldFilePath; -}; - bool startsWith(const std::string &Str, const std::string &Prefix) { return Str.size() >= Prefix.size() && Str.compare(0, Prefix.size(), Prefix) == 0; @@ -201,6 +162,191 @@ void processHunkBody(const std::string &Line, HunkContext &Ctx, } } +struct ModifyHunk { + ModifyHunk() = default; + ModifyHunk(std::string FilePath, unsigned Offset, unsigned Length, + std::string ReplacementText) + : FilePath(FilePath), Offset(Offset), Length(Length), + ReplacementText(ReplacementText) {}; + std::string FilePath; + unsigned Offset = 0; + unsigned Length = 0; + std::string ReplacementText; +}; + +struct AddHunk { + AddHunk() = default; + AddHunk(std::string NewFilePath) : NewFilePath(NewFilePath) {}; + std::string NewFilePath; +}; + +struct DeleteHunk { + DeleteHunk() = default; + DeleteHunk(std::string OldFilePath) : OldFilePath(OldFilePath) {}; + std::string OldFilePath; +}; + +struct MoveHunk { + MoveHunk() = default; + MoveHunk(std::string FilePath, unsigned Offset, unsigned Length, + std::string ReplacementText, std::string NewFilePath) + : FilePath(FilePath), Offset(Offset), Length(Length), + ReplacementText(ReplacementText), NewFilePath(NewFilePath) {}; + std::string FilePath; + unsigned Offset = 0; + unsigned Length = 0; + std::string ReplacementText; + std::string NewFilePath; +}; + +struct GitDiffChanges { + std::vector ModifyHunks; + std::vector AddHunks; + std::vector DeleteHunks; + std::vector MoveHunks; +}; +} // namespace + +LLVM_YAML_IS_SEQUENCE_VECTOR(ModifyHunk) +LLVM_YAML_IS_SEQUENCE_VECTOR(AddHunk) +LLVM_YAML_IS_SEQUENCE_VECTOR(DeleteHunk) +LLVM_YAML_IS_SEQUENCE_VECTOR(MoveHunk) +namespace llvm { +namespace yaml { +template <> struct MappingTraits { + struct NormalizedModifyFileHunk { + NormalizedModifyFileHunk(const IO &io) : Offset(0), Length(0) {} + NormalizedModifyFileHunk(const IO &io, ModifyHunk &H) + : FilePath(H.FilePath), Offset(H.Offset), Length(H.Length), + ReplacementText(H.ReplacementText) {} + ModifyHunk denormalize(const IO &) { + ModifyHunk H(FilePath, Offset, Length, ReplacementText); + return H; + } + std::string FilePath; + unsigned int Offset; + unsigned int Length; + std::string ReplacementText; + }; + + static void mapping(IO &Io, ModifyHunk &H) { + MappingNormalization Keys(Io, H); + Io.mapRequired("FilePath", Keys->FilePath); + Io.mapRequired("Offset", Keys->Offset); + Io.mapRequired("Length", Keys->Length); + Io.mapRequired("ReplacementText", Keys->ReplacementText); + } +}; + +template <> struct MappingTraits { + struct NormalizedAddFileHunk { + NormalizedAddFileHunk(const IO &io) {} + NormalizedAddFileHunk(const IO &io, AddHunk &H) + : NewFilePath(H.NewFilePath) {} + AddHunk denormalize(const IO &io) { + AddHunk H(NewFilePath); + return H; + } + std::string NewFilePath; + }; + static void mapping(IO &Io, AddHunk &H) { + MappingNormalization Keys(Io, H); + Io.mapRequired("NewFilePath", Keys->NewFilePath); + } +}; + +template <> struct MappingTraits { + struct NormalizedDeleteFileHunk { + NormalizedDeleteFileHunk(const IO &io) {} + NormalizedDeleteFileHunk(const IO &io, DeleteHunk &H) + : OldFilePath(H.OldFilePath) {} + DeleteHunk denormalize(const IO &io) { + DeleteHunk H(OldFilePath); + return H; + } + std::string OldFilePath; + }; + static void mapping(IO &Io, DeleteHunk &H) { + MappingNormalization Keys(Io, H); + Io.mapRequired("OldFilePath", Keys->OldFilePath); + } +}; + +template <> struct MappingTraits { + struct NormalizedMoveFileHunk { + NormalizedMoveFileHunk(const IO &io) : Offset(0), Length(0) {} + NormalizedMoveFileHunk(const IO &io, MoveHunk &H) + : FilePath(H.FilePath), Offset(H.Offset), Length(H.Length), + ReplacementText(H.ReplacementText), NewFilePath(H.NewFilePath) {} + MoveHunk denormalize(const IO &io) { + MoveHunk H(FilePath, Offset, Length, ReplacementText, NewFilePath); + return H; + } + std::string FilePath; + unsigned int Offset; + unsigned int Length; + std::string ReplacementText; + std::string NewFilePath; + }; + static void mapping(IO &Io, MoveHunk &H) { + MappingNormalization Keys(Io, H); + Io.mapRequired("FilePath", Keys->FilePath); + Io.mapRequired("Offset", Keys->Offset); + Io.mapRequired("Length", Keys->Length); + Io.mapRequired("ReplacementText", Keys->ReplacementText); + Io.mapRequired("NewFilePath", Keys->NewFilePath); + } +}; + +template <> struct MappingTraits { + static void mapping(IO &Io, GitDiffChanges &GDC) { + Io.mapOptional("ModifyFileHunks", GDC.ModifyHunks); + Io.mapOptional("AddFileHunks", GDC.AddHunks); + Io.mapOptional("DeleteFileHunks", GDC.DeleteHunks); + Io.mapOptional("MoveFileHunks", GDC.MoveHunks); + } +}; +} // namespace yaml +} // namespace llvm + +void printYaml(std::ostream &stream, const std::vector &Repls) { + GitDiffChanges GDC; + + for (const auto &R : Repls) { + if (R.OldFilePath == "/dev/null" && R.NewFilePath != "/dev/null") { + // Add replacement + AddHunk AH(R.NewFilePath); + GDC.AddHunks.push_back(AH); + continue; + } + if (R.OldFilePath != "/dev/null" && R.NewFilePath == "/dev/null") { + // Delete replacement + DeleteHunk DH(R.OldFilePath); + GDC.DeleteHunks.push_back(DH); + continue; + } + if (R.OldFilePath == R.NewFilePath && R.OldFilePath != "/dev/null") { + // Modify replacement + ModifyHunk MH(R.OldFilePath, R.Offset, R.Length, R.ReplacementText); + GDC.ModifyHunks.push_back(MH); + continue; + } + if (R.OldFilePath != R.NewFilePath) { + // Move replacement + MoveHunk MH(R.OldFilePath, R.Offset, R.Length, R.ReplacementText, + R.NewFilePath); + GDC.MoveHunks.push_back(MH); + continue; + } + throw std::runtime_error("Invalid replacement: " + R.OldFilePath + " -> " + + R.NewFilePath); + } + + llvm::raw_os_ostream raw_os(stream); + llvm::yaml::Output yout(raw_os); + yout << GDC; +} + std::vector parseDiff(const std::string &diffOutput, const std::string &RepoRoot) { std::vector replacements; @@ -265,162 +411,16 @@ std::vector parseDiff(const std::string &diffOutput, return replacements; } -struct ModifyHunk { - std::string FilePath; - unsigned Offset = 0; - unsigned Length = 0; - std::string ReplacementText; -}; - -struct AddHunk { - std::string NewFilePath; -}; - -struct DeleteHunk { - std::string OldFilePath; -}; - -struct MoveHunk { - std::string FilePath; - unsigned Offset = 0; - unsigned Length = 0; - std::string ReplacementText; - std::string NewFilePath; -}; - -void printYaml(std::ostream &stream, const std::vector &Repls) { - std::vector ModifyHunks; - std::vector AddHunks; - std::vector DeleteHunks; - std::vector MoveHunks; - - for (const auto &R : Repls) { - if (R.OldFilePath == "/dev/null" && R.NewFilePath != "/dev/null") { - // Add replacement - AddHunk AH; - AH.NewFilePath = R.NewFilePath; - AddHunks.push_back(AH); - continue; - } - if (R.OldFilePath != "/dev/null" && R.NewFilePath == "/dev/null") { - // Delete replacement - DeleteHunk DH; - DH.OldFilePath = R.OldFilePath; - DeleteHunks.push_back(DH); - continue; - } - if (R.OldFilePath == R.NewFilePath && R.OldFilePath != "/dev/null") { - // Modify replacement - ModifyHunk MH; - MH.FilePath = R.OldFilePath; - MH.Offset = R.Offset; - MH.Length = R.Length; - MH.ReplacementText = R.ReplacementText; - ModifyHunks.push_back(MH); - continue; - } - if (R.OldFilePath != R.NewFilePath) { - // Move replacement - MoveHunk MH; - MH.FilePath = R.OldFilePath; - MH.Offset = R.Offset; - MH.Length = R.Length; - MH.ReplacementText = R.ReplacementText; - MH.NewFilePath = R.NewFilePath; - MoveHunks.push_back(MH); - continue; - } - throw std::runtime_error("Invalid replacement: " + R.OldFilePath + " -> " + - R.NewFilePath); - } - - stream << "---" << std::endl; - if (!ModifyHunks.empty()) - stream << "ModifyFileHunks:" << std::endl; - for (const auto &H : ModifyHunks) { - stream << " - FilePath: " << "'" << H.FilePath << "'" << std::endl; - stream << " Offset: " << H.Offset << std::endl; - stream << " Length: " << H.Length << std::endl; - stream << " ReplacementText: " << "\"" << H.ReplacementText << "\"" - << std::endl; - } - if (!AddHunks.empty()) - stream << "AddFileHunks:" << std::endl; - for (const auto &H : AddHunks) { - stream << " - NewFilePath: " << "'" << H.NewFilePath << "'" - << std::endl; - } - if (!DeleteHunks.empty()) - stream << "DeleteFileHunks:" << std::endl; - for (const auto &H : DeleteHunks) { - stream << " - OldFilePath: " << "'" << H.OldFilePath << "'" - << std::endl; - } - if (!MoveHunks.empty()) - stream << "MoveFileHunks:" << std::endl; - for (const auto &H : MoveHunks) { - stream << " - FilePath: " << "'" << H.FilePath << "'" << std::endl; - stream << " Offset: " << H.Offset << std::endl; - stream << " Length: " << H.Length << std::endl; - stream << " ReplacementText: " << "\"" << H.ReplacementText << "\"" - << std::endl; - stream << " NewFilePath: " << "'" << H.NewFilePath << "'" - << std::endl; - } - stream << "..." << std::endl; -} - -} // namespace - -int main(int argc, char *argv[]) { - if (argc != 2 && argc != 4) { - std::cerr << "Usage: gitdiff2yaml [-o outputfile]" - << std::endl; - return 1; - } - bool OutputToFile = false; - if (argc == 4) { - if (std::string(argv[2]) != "-o") { - std::cerr << "Invalid option: " << argv[2] << std::endl; - return 1; - } - OutputToFile = true; +std::string execGitCommand(const std::string &CMD) { + std::array Buffer; + std::unique_ptr Pipe(popen(CMD.c_str(), "r"), pclose); + if (!Pipe) { + throw std::runtime_error("popen() failed!"); } - std::string OldCommitID = argv[1]; - - std::string RepoRoot = execGitCommand("git rev-parse --show-toplevel"); - RepoRoot = RepoRoot.substr(0, RepoRoot.size() - 1); // Remove the last '\n' - - std::string NewCommitID = execGitCommand("git log -1 --format=\"%H\""); - std::string DiffOutput = execGitCommand("git diff " + OldCommitID); - - execGitCommand("git reset --hard " + OldCommitID); - std::vector Repls = parseDiff(DiffOutput, RepoRoot); - - // Erase emtpy replacements - Repls.erase(std::remove_if(Repls.begin(), Repls.end(), - [](Replacement x) { - return (x.NewFilePath == "" && - x.OldFilePath == "" && x.Offset == 0 && - x.Length == 0 && - x.ReplacementText == ""); - }), - Repls.end()); - - if (OutputToFile) { - std::ofstream OutFile(argv[3]); - if (!OutFile.is_open()) { - std::cerr << "Failed to open output file: " << argv[3] << std::endl; - return 1; - } - printYaml(OutFile, Repls); - OutFile.close(); - } else { - printYaml(std::cout, Repls); + std::string Result; + while (fgets(Buffer.data(), Buffer.size(), Pipe.get()) != nullptr) { + Result += Buffer.data(); } - - execGitCommand("git reset --hard " + NewCommitID); - - return 0; + return Result; } diff --git a/clang/tools/gitdiff2yaml/src/gitdiff2yaml.h b/clang/tools/gitdiff2yaml/src/gitdiff2yaml.h new file mode 100644 index 000000000000..cc4dfa8de5b2 --- /dev/null +++ b/clang/tools/gitdiff2yaml/src/gitdiff2yaml.h @@ -0,0 +1,38 @@ +//===---------------------- gitdiff2yaml.h ----------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef __GITDIFF2YAML_H__ +#define __GITDIFF2YAML_H__ + +#include +#include + +const std::string LineEnd = "\n"; + +struct Replacement { + std::string NewFilePath; + std::string OldFilePath; + unsigned Offset = 0; + unsigned Length = 0; + std::string ReplacementText; +}; + +struct HunkContext { + unsigned OldCurrentLine = 0; + bool InHunk = false; + bool FastForward = false; + std::string CurrentNewFilePath; + std::string CurrentOldFilePath; +}; + +std::string execGitCommand(const std::string &CMD); +std::vector parseDiff(const std::string &diffOutput, + const std::string &RepoRoot); +void printYaml(std::ostream &stream, const std::vector &Repls); + +#endif // __GITDIFF2YAML_H__ diff --git a/clang/tools/gitdiff2yaml/src/main.cpp b/clang/tools/gitdiff2yaml/src/main.cpp new file mode 100644 index 000000000000..f441af43f248 --- /dev/null +++ b/clang/tools/gitdiff2yaml/src/main.cpp @@ -0,0 +1,103 @@ +//===------------------------- main.cpp -------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// Usage: +// $ cd /path/to/your/git/repo +// $ gitdiff2yaml -c -o +// This will output the clang replacements in YAML format. +// Limitation: +// (1) The workspace and the staging area should be clean before running +// this tool. +// (2) The line ending in the file should be '\n'. +//===----------------------------------------------------------------------===// + +#include "gitdiff2yaml.h" +#include "llvm/Support/CommandLine.h" + +#include +#include +#include +#include +#include + +static constexpr auto Description = R"--( + +This gitdiff2yaml tool converts the output of `git diff` into a YAML file +containing a list of replacements. +)--"; + +static constexpr auto Examples = R"--( +EXAMPLES: + +Output to the terminal: + + gitdiff2yaml -c + +Output to a file: + + gitdiff2yaml -c -o Changes.yaml + +)--"; + +static llvm::cl::OptionCategory &getG2YCategory() { + static llvm::cl::OptionCategory G2YCategory("gitdiff2yaml tool options"); + return G2YCategory; +} + +int main(int argc, char *argv[]) { + llvm::cl::opt OutputFilename( + "o", llvm::cl::desc("[optional] Specify output filename"), + llvm::cl::value_desc("filename"), llvm::cl::cat(getG2YCategory()), + llvm::cl::Optional); + + llvm::cl::opt OldCommitID( + "c", llvm::cl::desc("[required] Specify the old commit ID"), + llvm::cl::value_desc("commit_id"), llvm::cl::cat(getG2YCategory()), + llvm::cl::Required); + + llvm::cl::extrahelp MoreHelp(Examples); + + llvm::cl::HideUnrelatedOptions(getG2YCategory()); + + llvm::cl::ParseCommandLineOptions(argc, argv, Description); + + std::string RepoRoot = execGitCommand("git rev-parse --show-toplevel"); + RepoRoot = RepoRoot.substr(0, RepoRoot.size() - 1); // Remove the last '\n' + + std::string NewCommitID = execGitCommand("git log -1 --format=\"%H\""); + std::string DiffOutput = execGitCommand("git diff " + OldCommitID.getValue()); + + execGitCommand("git reset --hard " + OldCommitID.getValue()); + std::vector Repls = parseDiff(DiffOutput, RepoRoot); + + // Erase emtpy replacements + Repls.erase(std::remove_if(Repls.begin(), Repls.end(), + [](Replacement x) { + return (x.NewFilePath == "" && + x.OldFilePath == "" && x.Offset == 0 && + x.Length == 0 && + x.ReplacementText == ""); + }), + Repls.end()); + + if (!OutputFilename.empty()) { + std::ofstream OutFile(OutputFilename.getValue()); + if (!OutFile.is_open()) { + std::cerr << "Failed to open output file: " << OutputFilename.getValue() + << std::endl; + return 1; + } + printYaml(OutFile, Repls); + OutFile.close(); + } else { + printYaml(std::cout, Repls); + } + + execGitCommand("git reset --hard " + NewCommitID); + + return 0; +} diff --git a/clang/tools/dpct/gitdiff2yaml/test.py b/clang/tools/gitdiff2yaml/test.py similarity index 100% rename from clang/tools/dpct/gitdiff2yaml/test.py rename to clang/tools/gitdiff2yaml/test.py diff --git a/clang/tools/dpct/gitdiff2yaml/tests.zip b/clang/tools/gitdiff2yaml/tests.zip similarity index 100% rename from clang/tools/dpct/gitdiff2yaml/tests.zip rename to clang/tools/gitdiff2yaml/tests.zip From f4273848aec42298879fc07fbbe323c89697298b Mon Sep 17 00:00:00 2001 From: "Jiang, Zhiwei" Date: Tue, 8 Jul 2025 09:48:11 +0800 Subject: [PATCH 47/53] Update Signed-off-by: Jiang, Zhiwei --- clang/lib/DPCT/FileGenerator/GenFiles.cpp | 30 ++++++++++++----------- clang/lib/DPCT/FileGenerator/GenFiles.h | 3 --- 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/clang/lib/DPCT/FileGenerator/GenFiles.cpp b/clang/lib/DPCT/FileGenerator/GenFiles.cpp index 77fb3186c808..3b67b2b7f94a 100644 --- a/clang/lib/DPCT/FileGenerator/GenFiles.cpp +++ b/clang/lib/DPCT/FileGenerator/GenFiles.cpp @@ -190,18 +190,10 @@ bool rewriteCanonicalDir(clang::tooling::UnifiedPath &FilePath, return Result; } -void rewriteFileName(clang::tooling::UnifiedPath &FileName) { - rewriteFileName(FileName, FileName); -} - -void rewriteFileName(clang::tooling::UnifiedPath &FileName, - const clang::tooling::UnifiedPath &FullPathName) { - std::string FilePath = FileName.getPath().str(); - rewriteFileName(FilePath, FullPathName.getPath().str()); - FileName = FilePath; -} - -void rewriteFileName(std::string &FileName, const std::string &FullPathName) { +static void +rewriteFileName(std::string &FileName, const std::string &FullPathName, + std::function HasCUDASyntax = + DpctGlobalInfo::hasCUDASyntax) { SmallString<512> CanonicalPathStr(FullPathName); const auto Extension = path::extension(CanonicalPathStr); SourceProcessType FileType = GetSourceFileType(FullPathName); @@ -212,8 +204,7 @@ void rewriteFileName(std::string &FileName, const std::string &FullPathName) { if (FileType & SPT_CudaSource) { path::replace_extension(CanonicalPathStr, DpctGlobalInfo::getSYCLSourceExtension()); - } else if ((FileType & SPT_CppSource) && - DpctGlobalInfo::hasCUDASyntax(FileName)) { + } else if ((FileType & SPT_CppSource) && HasCUDASyntax(FileName)) { path::replace_extension(CanonicalPathStr, Extension + DpctGlobalInfo::getSYCLSourceExtension()); @@ -226,6 +217,17 @@ void rewriteFileName(std::string &FileName, const std::string &FullPathName) { FileName = CanonicalPathStr.c_str(); } +void rewriteFileName(clang::tooling::UnifiedPath &FileName, + const clang::tooling::UnifiedPath &FullPathName) { + std::string FilePath = FileName.getPath().str(); + rewriteFileName(FilePath, FullPathName.getPath().str()); + FileName = FilePath; +} + +void rewriteFileName(clang::tooling::UnifiedPath &FileName) { + rewriteFileName(FileName, FileName); +} + static std::vector FilesNotInCompilationDB; std::map OutFilePath2InFilePath; diff --git a/clang/lib/DPCT/FileGenerator/GenFiles.h b/clang/lib/DPCT/FileGenerator/GenFiles.h index 828d5c439aa8..1ba8fdda4830 100644 --- a/clang/lib/DPCT/FileGenerator/GenFiles.h +++ b/clang/lib/DPCT/FileGenerator/GenFiles.h @@ -76,9 +76,6 @@ void rewriteFileName(clang::tooling::UnifiedPath &FileName); void rewriteFileName(clang::tooling::UnifiedPath &FileName, const clang::tooling::UnifiedPath &FullPathName); -// Replace file name \p FileName with new migrated name. -void rewriteFileName(std::string &FileName, const std::string &FullPathName); - // A mapping from output file path to it's corresponding input file. extern std::map OutFilePath2InFilePath; From 2789382d8ff22cde95036dec42e8b11217a86ac7 Mon Sep 17 00:00:00 2001 From: "Jiang, Zhiwei" Date: Mon, 7 Jul 2025 09:15:27 +0800 Subject: [PATCH 48/53] [SYCLomatic] Fix CMake script migration Signed-off-by: Jiang, Zhiwei --- clang/lib/DPCT/DPCT.cpp | 5 ----- clang/lib/DPCT/UserDefinedRules/PatternRewriter.cpp | 9 ++++++--- clang/lib/DPCT/UserDefinedRules/PatternRewriter.h | 1 - 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/clang/lib/DPCT/DPCT.cpp b/clang/lib/DPCT/DPCT.cpp index 43ad411e394f..88ba45658b0d 100644 --- a/clang/lib/DPCT/DPCT.cpp +++ b/clang/lib/DPCT/DPCT.cpp @@ -520,11 +520,6 @@ static void loadMainSrcFileInfo(clang::tooling::UnifiedPath YamlFilePath) { } DpctGlobalInfo::setMainSourceYamlTUR(PreTU); - for (auto &Entry : PreTU->MainSourceFilesDigest) { - if (Entry.HasCUDASyntax) - MainSrcFilesHasCudaSyntex.insert(Entry.MainSourceFile); - } - // Currently, when "--use-experimental-features=device_global" and // "--use-experimental-features=all" are specified, the migrated code should // be compiled with C++20 or later. diff --git a/clang/lib/DPCT/UserDefinedRules/PatternRewriter.cpp b/clang/lib/DPCT/UserDefinedRules/PatternRewriter.cpp index 9825631020e0..911c8d86759a 100644 --- a/clang/lib/DPCT/UserDefinedRules/PatternRewriter.cpp +++ b/clang/lib/DPCT/UserDefinedRules/PatternRewriter.cpp @@ -28,7 +28,6 @@ namespace clang { namespace dpct { -std::set MainSrcFilesHasCudaSyntex; bool LANG_Cplusplus_20_Used = false; struct SpacingElement {}; @@ -465,9 +464,13 @@ static void applyExtenstionNameChange( std::string SrcFile = Input.substr(Pos, Next + ExtensionType.length() + 1 /*strlen of "."*/ - Pos); bool HasCudaSyntax = false; - + std::set MainSrcFilesHasCudaSyntex; + for (auto &Entry : + DpctGlobalInfo::getMainSourceYamlTUR()->MainSourceFilesDigest) { + if (Entry.HasCUDASyntax) + MainSrcFilesHasCudaSyntex.insert(Entry.MainSourceFile); + } for (const auto &_File : MainSrcFilesHasCudaSyntex) { - llvm::SmallString<512> File(_File); llvm::sys::path::native(File); diff --git a/clang/lib/DPCT/UserDefinedRules/PatternRewriter.h b/clang/lib/DPCT/UserDefinedRules/PatternRewriter.h index 144f53c415a7..ac8128c6b2c6 100644 --- a/clang/lib/DPCT/UserDefinedRules/PatternRewriter.h +++ b/clang/lib/DPCT/UserDefinedRules/PatternRewriter.h @@ -27,7 +27,6 @@ bool fixLineEndings(const std::string &Input, std::string &Output); enum SourceFileType { SFT_CAndCXXSource, SFT_CMakeScript, SFT_PySetupScript }; void setFileTypeProcessed(enum SourceFileType FileType); -extern std::set MainSrcFilesHasCudaSyntex; extern bool LANG_Cplusplus_20_Used; } // namespace dpct From 29c72b703960ba9f7476e9d402d7afab691ff646 Mon Sep 17 00:00:00 2001 From: "Jiang, Zhiwei" Date: Tue, 8 Jul 2025 14:21:36 +0800 Subject: [PATCH 49/53] Fix Signed-off-by: Jiang, Zhiwei --- clang/lib/DPCT/FileGenerator/GenFiles.cpp | 23 ++++--- clang/lib/DPCT/IncMigration/ReMigration.cpp | 21 +++--- clang/lib/DPCT/IncMigration/ReMigration.h | 23 ++++--- .../DPCT/UserDefinedRules/PatternRewriter.cpp | 3 +- .../test/dpct/remigration/LastMigration.yaml | 66 +++++++++---------- 5 files changed, 71 insertions(+), 65 deletions(-) diff --git a/clang/lib/DPCT/FileGenerator/GenFiles.cpp b/clang/lib/DPCT/FileGenerator/GenFiles.cpp index 3b67b2b7f94a..f93ac16bc1ca 100644 --- a/clang/lib/DPCT/FileGenerator/GenFiles.cpp +++ b/clang/lib/DPCT/FileGenerator/GenFiles.cpp @@ -49,6 +49,7 @@ extern DpctOption BuildScriptFile; extern DpctOption GenBuildScript; extern std::map ErrorCnt; bool ReMigrationReady = false; +extern std::set MainSrcFilesHasCudaSyntex; namespace clang { namespace tooling { @@ -528,22 +529,28 @@ int writeReplacementsToFiles( if (ReMigrationReady) { std::vector Repl_B; std::vector Repl_C1; - // TODO: test only - std::map - FileNameMap; for (const auto &Entry : Replset2) { - clang::tooling::UnifiedPath Path = Entry.first; - rewriteFileName(Path); - FileNameMap[Path] = Entry.first; for (const auto &Repl : Entry.second) { Repl_B.push_back(Repl); } } - for (const auto &Repl : clang::dpct::getLastMigration()->Replacements) { Repl_C1.push_back(Repl); } + + std::map FileNameMap; + auto hasCUDASyntax = [](tooling::UnifiedPath Path) -> bool { + if (MainSrcFilesHasCudaSyntex.find(Path.getCanonicalPath().str()) != + MainSrcFilesHasCudaSyntex.end()) + return true; + return false; + }; + for (const auto &Entry : Repl_C1) { + std::string CUDAFilePath = Entry.getFilePath().str(); + std::string SYCLFilePath; + rewriteFileName(SYCLFilePath, CUDAFilePath, hasCUDASyntax); + FileNameMap[SYCLFilePath] = CUDAFilePath; + } std::map> Result = clang::dpct::reMigrationMerge(clang::dpct::getUpstreamChanges(), Repl_B, Repl_C1, clang::dpct::getUserChanges(), diff --git a/clang/lib/DPCT/IncMigration/ReMigration.cpp b/clang/lib/DPCT/IncMigration/ReMigration.cpp index 8f11051fae09..463c404cd8c9 100644 --- a/clang/lib/DPCT/IncMigration/ReMigration.cpp +++ b/clang/lib/DPCT/IncMigration/ReMigration.cpp @@ -621,19 +621,18 @@ mergeC1AndC2Impl(const std::vector &A, // Merge Repl_C1 and Repl_C2. If has conflict, keep repl from Repl_C2. std::vector mergeC1AndC2( const std::vector &Repl_C1, const GitDiffChanges &Repl_C2, - const std::map + const std::map &FileNameMap) { std::vector Result; std::vector Repl_C2_vec; - std::for_each(Repl_C2.ModifyFileHunks.begin(), Repl_C2.ModifyFileHunks.end(), - [&Repl_C2_vec, FileNameMap](const Replacement &Hunk) { - UnifiedPath OldFilePath = - FileNameMap.at(UnifiedPath(Hunk.getFilePath())); - Replacement Replacement(OldFilePath.getCanonicalPath(), - Hunk.getOffset(), Hunk.getLength(), - Hunk.getReplacementText()); - Repl_C2_vec.push_back(Replacement); - }); + std::for_each( + Repl_C2.ModifyFileHunks.begin(), Repl_C2.ModifyFileHunks.end(), + [&Repl_C2_vec, FileNameMap](const Replacement &Hunk) { + std::string OldFilePath = FileNameMap.at(Hunk.getFilePath().str()); + Replacement Replacement(OldFilePath, Hunk.getOffset(), Hunk.getLength(), + Hunk.getReplacementText()); + Repl_C2_vec.push_back(Replacement); + }); auto C1 = groupReplcementsByFile(Repl_C1); auto C2 = groupReplcementsByFile(Repl_C2_vec); @@ -698,7 +697,7 @@ std::vector mergeC1AndC2( std::map> reMigrationMerge( const GitDiffChanges &Repl_A, const std::vector &Repl_B, const std::vector &Repl_C1, const GitDiffChanges &Repl_C2, - const std::map + const std::map &FileNameMap) { // TODO: is this assumption true? Will the manual fix delete/add/move file? assert(Repl_C2.AddFileHunks.empty() && Repl_C2.DeleteFileHunks.empty() && diff --git a/clang/lib/DPCT/IncMigration/ReMigration.h b/clang/lib/DPCT/IncMigration/ReMigration.h index ccd09b9057e3..d49b1b5e8167 100644 --- a/clang/lib/DPCT/IncMigration/ReMigration.h +++ b/clang/lib/DPCT/IncMigration/ReMigration.h @@ -127,20 +127,19 @@ std::vector mergeMapsByLine(const std::map &MapA, const std::map> &MapB, const clang::tooling::UnifiedPath &FilePath); -std::vector -mergeC1AndC2(const std::vector &Repl_C1, - const GitDiffChanges &Repl_C2, - const std::map &FileNameMap); - -std::map> -reMigrationMerge( - const GitDiffChanges &Repl_A, - const std::vector &Repl_B, +std::vector mergeC1AndC2( const std::vector &Repl_C1, const GitDiffChanges &Repl_C2, - const std::map &FileNameMap); + const std::map + &FileNameMap); + +std::map> +reMigrationMerge(const GitDiffChanges &Repl_A, + const std::vector &Repl_B, + const std::vector &Repl_C1, + const GitDiffChanges &Repl_C2, + const std::map &FileNameMap); } // namespace dpct } // namespace clang diff --git a/clang/lib/DPCT/UserDefinedRules/PatternRewriter.cpp b/clang/lib/DPCT/UserDefinedRules/PatternRewriter.cpp index 911c8d86759a..4dea8b6799cf 100644 --- a/clang/lib/DPCT/UserDefinedRules/PatternRewriter.cpp +++ b/clang/lib/DPCT/UserDefinedRules/PatternRewriter.cpp @@ -25,6 +25,8 @@ #include #include +std::set MainSrcFilesHasCudaSyntex; + namespace clang { namespace dpct { @@ -464,7 +466,6 @@ static void applyExtenstionNameChange( std::string SrcFile = Input.substr(Pos, Next + ExtensionType.length() + 1 /*strlen of "."*/ - Pos); bool HasCudaSyntax = false; - std::set MainSrcFilesHasCudaSyntex; for (auto &Entry : DpctGlobalInfo::getMainSourceYamlTUR()->MainSourceFilesDigest) { if (Entry.HasCUDASyntax) diff --git a/clang/test/dpct/remigration/LastMigration.yaml b/clang/test/dpct/remigration/LastMigration.yaml index b3db45c35693..b06b3c4fb633 100644 --- a/clang/test/dpct/remigration/LastMigration.yaml +++ b/clang/test/dpct/remigration/LastMigration.yaml @@ -1,7 +1,7 @@ --- -MainSourceFile: '/home/zhiwei/testfolder/test/MainSrcFiles_placehold' +MainSourceFile: 'PATH_PLACEHOLDER/MainSrcFiles_placehold' Replacements: - - FilePath: 'test.cu' + - FilePath: 'PATH_PLACEHOLDER/test.cu' Offset: 0 Length: 26 ReplacementText: "#include \n#include \n" @@ -10,7 +10,7 @@ Replacements: InitStr: '' NewHostVarName: '' BlockLevelFormatFlag: false - - FilePath: 'test.cu' + - FilePath: 'PATH_PLACEHOLDER/test.cu' Offset: 45 Length: 0 ReplacementText: "/*\nDPCT1009:3: SYCL reports errors using exceptions and does not use error codes. Please replace the \"get_error_string_dummy(...)\" with a real error-handling function.\n*/\n" @@ -19,7 +19,7 @@ Replacements: InitStr: '' NewHostVarName: '' BlockLevelFormatFlag: false - - FilePath: 'test.cu' + - FilePath: 'PATH_PLACEHOLDER/test.cu' Offset: 211 Length: 11 ReplacementText: 'dpct::err0' @@ -28,7 +28,7 @@ Replacements: InitStr: '' NewHostVarName: '' BlockLevelFormatFlag: false - - FilePath: 'test.cu' + - FilePath: 'PATH_PLACEHOLDER/test.cu' Offset: 292 Length: 325 ReplacementText: '' @@ -37,7 +37,7 @@ Replacements: InitStr: '' NewHostVarName: '' BlockLevelFormatFlag: false - - FilePath: 'test.cu' + - FilePath: 'PATH_PLACEHOLDER/test.cu' Offset: 708 Length: 11 ReplacementText: '' @@ -46,7 +46,7 @@ Replacements: InitStr: '' NewHostVarName: '' BlockLevelFormatFlag: false - - FilePath: 'test.cu' + - FilePath: 'PATH_PLACEHOLDER/test.cu' Offset: 782 Length: 0 ReplacementText: "auto item_ct1 = sycl::ext::oneapi::this_work_item::get_nd_item<3>();\n " @@ -55,7 +55,7 @@ Replacements: InitStr: '' NewHostVarName: '' BlockLevelFormatFlag: false - - FilePath: 'test.cu' + - FilePath: 'PATH_PLACEHOLDER/test.cu' Offset: 792 Length: 10 ReplacementText: 'item_ct1.get_group(2)' @@ -64,7 +64,7 @@ Replacements: InitStr: '' NewHostVarName: '' BlockLevelFormatFlag: false - - FilePath: 'test.cu' + - FilePath: 'PATH_PLACEHOLDER/test.cu' Offset: 805 Length: 10 ReplacementText: 'item_ct1.get_local_range(2)' @@ -73,7 +73,7 @@ Replacements: InitStr: '' NewHostVarName: '' BlockLevelFormatFlag: false - - FilePath: 'test.cu' + - FilePath: 'PATH_PLACEHOLDER/test.cu' Offset: 818 Length: 11 ReplacementText: 'item_ct1.get_local_id(2)' @@ -82,7 +82,7 @@ Replacements: InitStr: '' NewHostVarName: '' BlockLevelFormatFlag: false - - FilePath: 'test.cu' + - FilePath: 'PATH_PLACEHOLDER/test.cu' Offset: 931 Length: 0 ReplacementText: ' try ' @@ -91,7 +91,7 @@ Replacements: InitStr: '' NewHostVarName: '' BlockLevelFormatFlag: false - - FilePath: 'test.cu' + - FilePath: 'PATH_PLACEHOLDER/test.cu' Offset: 932 Length: 0 ReplacementText: "\n dpct::device_ext &dev_ct1 = dpct::get_current_device();\n sycl::queue &q_ct1 = dev_ct1.in_order_queue();" @@ -100,7 +100,7 @@ Replacements: InitStr: '' NewHostVarName: '' BlockLevelFormatFlag: false - - FilePath: 'test.cu' + - FilePath: 'PATH_PLACEHOLDER/test.cu' Offset: 975 Length: 42 ReplacementText: 'DPCT_CHECK_ERROR(input_d = sycl::malloc_device(size, q_ct1))' @@ -109,7 +109,7 @@ Replacements: InitStr: '' NewHostVarName: '' BlockLevelFormatFlag: false - - FilePath: 'test.cu' + - FilePath: 'PATH_PLACEHOLDER/test.cu' Offset: 1033 Length: 43 ReplacementText: 'DPCT_CHECK_ERROR(output_d = sycl::malloc_device(size, q_ct1))' @@ -118,7 +118,7 @@ Replacements: InitStr: '' NewHostVarName: '' BlockLevelFormatFlag: false - - FilePath: 'test.cu' + - FilePath: 'PATH_PLACEHOLDER/test.cu' Offset: 1099 Length: 71 ReplacementText: 'DPCT_CHECK_ERROR(q_ct1.memcpy(input_d, data, size * sizeof(float)).wait())' @@ -127,7 +127,7 @@ Replacements: InitStr: '' NewHostVarName: '' BlockLevelFormatFlag: false - - FilePath: 'test.cu' + - FilePath: 'PATH_PLACEHOLDER/test.cu' Offset: 1251 Length: 0 ReplacementText: " /*\n DPCT1049:0: The work-group size passed to the SYCL kernel may exceed the limit. To get the device limit, query info::device::max_work_group_size. Adjust the work-group size if needed.\n */\n" @@ -136,7 +136,7 @@ Replacements: InitStr: '' NewHostVarName: '' BlockLevelFormatFlag: false - - FilePath: 'test.cu' + - FilePath: 'PATH_PLACEHOLDER/test.cu' Offset: 1253 Length: 64 ReplacementText: "q_ct1.parallel_for(\n sycl::nd_range<3>(sycl::range<3>(1, 1, numBlocks) * sycl::range<3>(1, 1, blockSize), sycl::range<3>(1, 1, blockSize)), \n [=](sycl::nd_item<3> item_ct1) {\n scale_kernel(input_d, scale, output_d);\n });" @@ -145,7 +145,7 @@ Replacements: InitStr: '' NewHostVarName: '' BlockLevelFormatFlag: true - - FilePath: 'test.cu' + - FilePath: 'PATH_PLACEHOLDER/test.cu' Offset: 1317 Length: 1 ReplacementText: '' @@ -154,7 +154,7 @@ Replacements: InitStr: '' NewHostVarName: '' BlockLevelFormatFlag: false - - FilePath: 'test.cu' + - FilePath: 'PATH_PLACEHOLDER/test.cu' Offset: 1332 Length: 23 ReplacementText: 'DPCT_CHECK_ERROR(dev_ct1.queues_wait_and_throw())' @@ -163,7 +163,7 @@ Replacements: InitStr: '' NewHostVarName: '' BlockLevelFormatFlag: false - - FilePath: 'test.cu' + - FilePath: 'PATH_PLACEHOLDER/test.cu' Offset: 1358 Length: 0 ReplacementText: " /*\n DPCT1010:4: SYCL uses exceptions to report errors and does not use the error codes. The cudaGetLastError function call was replaced with 0. You need to rewrite this code.\n */\n" @@ -172,7 +172,7 @@ Replacements: InitStr: '' NewHostVarName: '' BlockLevelFormatFlag: false - - FilePath: 'test.cu' + - FilePath: 'PATH_PLACEHOLDER/test.cu' Offset: 1360 Length: 11 ReplacementText: 'dpct::err0' @@ -181,7 +181,7 @@ Replacements: InitStr: '' NewHostVarName: '' BlockLevelFormatFlag: false - - FilePath: 'test.cu' + - FilePath: 'PATH_PLACEHOLDER/test.cu' Offset: 1378 Length: 18 ReplacementText: '0' @@ -190,7 +190,7 @@ Replacements: InitStr: '' NewHostVarName: '' BlockLevelFormatFlag: false - - FilePath: 'test.cu' + - FilePath: 'PATH_PLACEHOLDER/test.cu' Offset: 1398 Length: 0 ReplacementText: " /*\n DPCT1000:2: Error handling if-stmt was detected but could not be rewritten.\n */\n" @@ -199,7 +199,7 @@ Replacements: InitStr: '' NewHostVarName: '' BlockLevelFormatFlag: false - - FilePath: 'test.cu' + - FilePath: 'PATH_PLACEHOLDER/test.cu' Offset: 1411 Length: 11 ReplacementText: '0' @@ -208,7 +208,7 @@ Replacements: InitStr: '' NewHostVarName: '' BlockLevelFormatFlag: false - - FilePath: 'test.cu' + - FilePath: 'PATH_PLACEHOLDER/test.cu' Offset: 1426 Length: 0 ReplacementText: " /*\n DPCT1009:5: SYCL reports errors using exceptions and does not use error codes. Please replace the \"get_error_string_dummy(...)\" with a real error-handling function.\n */\n" @@ -217,7 +217,7 @@ Replacements: InitStr: '' NewHostVarName: '' BlockLevelFormatFlag: false - - FilePath: 'test.cu' + - FilePath: 'PATH_PLACEHOLDER/test.cu' Offset: 1481 Length: 23 ReplacementText: 'dpct::get_error_string_dummy(err)' @@ -226,7 +226,7 @@ Replacements: InitStr: '' NewHostVarName: '' BlockLevelFormatFlag: false - - FilePath: 'test.cu' + - FilePath: 'PATH_PLACEHOLDER/test.cu' Offset: 1507 Length: 0 ReplacementText: " /*\n DPCT1001:1: The statement could not be removed.\n */\n" @@ -235,7 +235,7 @@ Replacements: InitStr: '' NewHostVarName: '' BlockLevelFormatFlag: false - - FilePath: 'test.cu' + - FilePath: 'PATH_PLACEHOLDER/test.cu' Offset: 1511 Length: 17 ReplacementText: 'dpct::dpct_free(input_d, q_ct1)' @@ -244,7 +244,7 @@ Replacements: InitStr: '' NewHostVarName: '' BlockLevelFormatFlag: false - - FilePath: 'test.cu' + - FilePath: 'PATH_PLACEHOLDER/test.cu' Offset: 1534 Length: 18 ReplacementText: 'dpct::dpct_free(output_d, q_ct1)' @@ -253,7 +253,7 @@ Replacements: InitStr: '' NewHostVarName: '' BlockLevelFormatFlag: false - - FilePath: 'test.cu' + - FilePath: 'PATH_PLACEHOLDER/test.cu' Offset: 1596 Length: 98 ReplacementText: 'DPCT_CHECK_ERROR(q_ct1.memcpy(output, output_d, size * sizeof(float)).wait())' @@ -262,7 +262,7 @@ Replacements: InitStr: '' NewHostVarName: '' BlockLevelFormatFlag: false - - FilePath: 'test.cu' + - FilePath: 'PATH_PLACEHOLDER/test.cu' Offset: 1710 Length: 17 ReplacementText: 'DPCT_CHECK_ERROR(dpct::dpct_free(input_d, q_ct1))' @@ -271,7 +271,7 @@ Replacements: InitStr: '' NewHostVarName: '' BlockLevelFormatFlag: false - - FilePath: 'test.cu' + - FilePath: 'PATH_PLACEHOLDER/test.cu' Offset: 1743 Length: 18 ReplacementText: 'DPCT_CHECK_ERROR(dpct::dpct_free(output_d, q_ct1))' @@ -280,7 +280,7 @@ Replacements: InitStr: '' NewHostVarName: '' BlockLevelFormatFlag: false - - FilePath: 'test.cu' + - FilePath: 'PATH_PLACEHOLDER/test.cu' Offset: 1765 Length: 0 ReplacementText: "\ncatch (sycl::exception const &exc) {\n std::cerr << exc.what() << \"Exception caught at file:\" << __FILE__ << \", line:\" << __LINE__ << std::endl;\n std::exit(1);\n}" From 26444ff924115363e9eaaba661b0d9f2cc29b02e Mon Sep 17 00:00:00 2001 From: "Jiang, Zhiwei" Date: Wed, 9 Jul 2025 10:43:12 +0800 Subject: [PATCH 50/53] Fix unittest Signed-off-by: Jiang, Zhiwei --- clang/unittests/DPCT/CMakeLists.txt | 11 +++-------- clang/unittests/DPCT/ReMigrationTest.cpp | 2 +- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/clang/unittests/DPCT/CMakeLists.txt b/clang/unittests/DPCT/CMakeLists.txt index 2e2f8f04df62..1d36997625d4 100644 --- a/clang/unittests/DPCT/CMakeLists.txt +++ b/clang/unittests/DPCT/CMakeLists.txt @@ -1,17 +1,12 @@ -set(LLVM_LINK_COMPONENTS - Support - ) - add_clang_unittest(DpctTests ReMigrationTest.cpp - ) - -target_link_libraries(DpctTests - PRIVATE + CLANG_LIBS DPCT clangBasic clangFrontend clangRewrite clangTooling clangToolingCore + LLVM_COMPONENTS + Support ) diff --git a/clang/unittests/DPCT/ReMigrationTest.cpp b/clang/unittests/DPCT/ReMigrationTest.cpp index 8e727c6f82f3..21f0f520a4a5 100644 --- a/clang/unittests/DPCT/ReMigrationTest.cpp +++ b/clang/unittests/DPCT/ReMigrationTest.cpp @@ -375,7 +375,7 @@ bbyyy2345678xxxc78www0123456789 Replacement("file1.dp.cpp", 16, 2, "yyy"), Replacement("file1.dp.cpp", 4, 0, "zzz"), }; - const std::map FileNameMap = { + const std::map FileNameMap = { {"file1.dp.cpp", "file1.cu"}}; std::vector Result = From 8b181fc40d2cddd1d80759da1e02ad92dbdaca1f Mon Sep 17 00:00:00 2001 From: "Jiang, Zhiwei" Date: Wed, 9 Jul 2025 12:50:35 +0800 Subject: [PATCH 51/53] Update fix Signed-off-by: Jiang, Zhiwei --- clang/unittests/DPCT/ReMigrationTest.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/clang/unittests/DPCT/ReMigrationTest.cpp b/clang/unittests/DPCT/ReMigrationTest.cpp index 21f0f520a4a5..28c4aa6ccab1 100644 --- a/clang/unittests/DPCT/ReMigrationTest.cpp +++ b/clang/unittests/DPCT/ReMigrationTest.cpp @@ -376,7 +376,8 @@ bbyyy2345678xxxc78www0123456789 Replacement("file1.dp.cpp", 4, 0, "zzz"), }; const std::map FileNameMap = { - {"file1.dp.cpp", "file1.cu"}}; + {UnifiedPath("file1.dp.cpp").getCanonicalPath().str(), + UnifiedPath("file1.cu").getCanonicalPath().str()}}; std::vector Result = mergeC1AndC2(Repl_C1, Repl_C2, FileNameMap); From 39dea431a1e10d70f5aa5447579c07e7ec23c008 Mon Sep 17 00:00:00 2001 From: "Jiang, Zhiwei" Date: Wed, 9 Jul 2025 12:51:31 +0800 Subject: [PATCH 52/53] remove some tests Signed-off-by: Jiang, Zhiwei --- clang/tools/gitdiff2yaml/1.ref.txt | 5 -- clang/tools/gitdiff2yaml/2.ref.txt | 4 -- clang/tools/gitdiff2yaml/3.ref.txt | 8 --- clang/tools/gitdiff2yaml/4.ref.txt | 27 -------- clang/tools/gitdiff2yaml/test.py | 101 ----------------------------- clang/tools/gitdiff2yaml/tests.zip | Bin 92968 -> 0 bytes 6 files changed, 145 deletions(-) delete mode 100644 clang/tools/gitdiff2yaml/1.ref.txt delete mode 100644 clang/tools/gitdiff2yaml/2.ref.txt delete mode 100644 clang/tools/gitdiff2yaml/3.ref.txt delete mode 100644 clang/tools/gitdiff2yaml/4.ref.txt delete mode 100644 clang/tools/gitdiff2yaml/test.py delete mode 100644 clang/tools/gitdiff2yaml/tests.zip diff --git a/clang/tools/gitdiff2yaml/1.ref.txt b/clang/tools/gitdiff2yaml/1.ref.txt deleted file mode 100644 index 23273daa15be..000000000000 --- a/clang/tools/gitdiff2yaml/1.ref.txt +++ /dev/null @@ -1,5 +0,0 @@ ---- -AddFileHunks: - - NewFilePath: 'b.txt' - - NewFilePath: 'c.txt' -... diff --git a/clang/tools/gitdiff2yaml/2.ref.txt b/clang/tools/gitdiff2yaml/2.ref.txt deleted file mode 100644 index 6da1bae78ec3..000000000000 --- a/clang/tools/gitdiff2yaml/2.ref.txt +++ /dev/null @@ -1,4 +0,0 @@ ---- -DeleteFileHunks: - - OldFilePath: 'b.txt' -... diff --git a/clang/tools/gitdiff2yaml/3.ref.txt b/clang/tools/gitdiff2yaml/3.ref.txt deleted file mode 100644 index 7579b9fee24b..000000000000 --- a/clang/tools/gitdiff2yaml/3.ref.txt +++ /dev/null @@ -1,8 +0,0 @@ ---- -MoveFileHunks: - - FilePath: 'b.txt' - Offset: 12 - Length: 4 - ReplacementText: "777\n" - NewFilePath: 'c.txt' -... diff --git a/clang/tools/gitdiff2yaml/4.ref.txt b/clang/tools/gitdiff2yaml/4.ref.txt deleted file mode 100644 index 58fe43dd35c5..000000000000 --- a/clang/tools/gitdiff2yaml/4.ref.txt +++ /dev/null @@ -1,27 +0,0 @@ ---- -ModifyFileHunks: - - FilePath: 'a.txt' - Offset: 8 - Length: 8 - ReplacementText: "111\n222\n" - - FilePath: 'a.txt' - Offset: 20 - Length: 4 - ReplacementText: "333\n" - - FilePath: 'a.txt' - Offset: 56 - Length: 0 - ReplacementText: "444\n" - - FilePath: 'a.txt' - Offset: 92 - Length: 4 - ReplacementText: "" - - FilePath: 'b.txt' - Offset: 40 - Length: 12 - ReplacementText: "" - - FilePath: 'b.txt' - Offset: 92 - Length: 0 - ReplacementText: "111\n222\n333\n" -... diff --git a/clang/tools/gitdiff2yaml/test.py b/clang/tools/gitdiff2yaml/test.py deleted file mode 100644 index 7c4dc291cb5b..000000000000 --- a/clang/tools/gitdiff2yaml/test.py +++ /dev/null @@ -1,101 +0,0 @@ -# ===----- test.py --------------------------------------------------------=== # -# -# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -# See https://llvm.org/LICENSE.txt for license information. -# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -# -# ===----------------------------------------------------------------------=== # - -from pathlib import Path -import os -import shutil -import subprocess - -def run_command(cmd): - try: - result = subprocess.run( - cmd, - check=True, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - text=True - ) - return result.stdout - except subprocess.CalledProcessError as e: - print(f"Command fail: {e.stderr}") - return None - -def compare_with_diff(ref_file): - result = subprocess.run( - ["diff", "-u", ref_file, "temp.txt"], - capture_output=True, - text=True - ) - - if result.returncode == 0: - return True - else: - print("Found difference:\n" + result.stdout) - return False - -# Clean up previous test directory if it exists -test_dir = Path("tests") -if test_dir.exists() and test_dir.is_dir(): - shutil.rmtree(test_dir) - -# Check if gitdiff2yaml exists -gitdiff2yaml_path = Path("gitdiff2yaml") -if not gitdiff2yaml_path.exists(): - print("gitdiff2yaml does not exist. Please build it first.") - exit(1) - -run_command(["unzip", "tests.zip"]) -os.chdir("tests") - -# Test 1 -os.chdir("1") -old_commit_id = run_command(["git", "rev-parse", "HEAD~1"]).strip() -result = run_command(["../../gitdiff2yaml", old_commit_id, "-o", "temp.txt"]) -test1_res = compare_with_diff("../../1.ref.txt") -if test1_res: - print("Test 1 passed") -else: - print("Test 1 failed") -os.chdir("..") - -# Test 2 -os.chdir("2") -old_commit_id = run_command(["git", "rev-parse", "HEAD~1"]).strip() -result = run_command(["../../gitdiff2yaml", old_commit_id, "-o", "temp.txt"]) -test1_res = compare_with_diff("../../2.ref.txt") -if test1_res: - print("Test 2 passed") -else: - print("Test 2 failed") -os.chdir("..") - -# Test 3 -os.chdir("3") -old_commit_id = run_command(["git", "rev-parse", "HEAD~1"]).strip() -result = run_command(["../../gitdiff2yaml", old_commit_id, "-o", "temp.txt"]) -test1_res = compare_with_diff("../../3.ref.txt") -if test1_res: - print("Test 3 passed") -else: - print("Test 3 failed") -os.chdir("..") - -# Test 4 -os.chdir("4") -old_commit_id = run_command(["git", "rev-parse", "HEAD~1"]).strip() -result = run_command(["../../gitdiff2yaml", old_commit_id, "-o", "temp.txt"]) -test1_res = compare_with_diff("../../4.ref.txt") -if test1_res: - print("Test 4 passed") -else: - print("Test 4 failed") -os.chdir("..") - -# Clean up -os.chdir("..") -shutil.rmtree(test_dir) diff --git a/clang/tools/gitdiff2yaml/tests.zip b/clang/tools/gitdiff2yaml/tests.zip deleted file mode 100644 index dec688f804e831f90939d1b192432d2f6d40da42..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 92968 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%0A%B0wk~k z-jE1R)*ALGP3=s{A!1hAj1Z_!H!ELZs29Vmw8?=z&+qR-X`$(K*v#|KnX_OVObw#rxvvDH!@#ae)xmBrLX2TV4u?O+O<6``AzH;KAdv$B|Jqh{sJZdgeyW+heF%$sgj zrgbL@lg&y8k?3Zn*qa!MZdN*oMmH=bxkQk9=Ryv4BH!H_INsSk6Ryv48H!C*}r$u6zl@21(&C0S*6C<+C zN(T|?X62Nz#E2BL(m^P?Svds!gac{9kp4EqM&U+HG$kI!S#id-S8tZ^%W+nSDY(yX0ee^(t?L8w{z@Kj8e zU{*TWbx591z>d|^7hI8>pkX^bH4O`3F)OK=8wJ7L5_D5gZaOAxnw2*G0IrMfB6|{b z%`Iv%#Zj~JUAnksD@hGu_GnDt^(k+IVz&fUM$O7wb|EZgrA^Pmp<@jc+3^`Y>zPUF zfTCeZ)iyeJ2hiSg_k-!MN$`pocZPO>McXthGuTg5@TV(*QqdpOtn4x?lCEgD6!6z8 zv0rpB%u1um&4JpA-glja!}dumMo1;`Bsah>3_msNF-)SmPGx}nAv8Qh)G%aylEqyhGmPoF5PL; zQuv8Fot-#TulCKX$_l6hywE4=r403e;8TK%^%~G z5!m_)0;<>8R(&9Oj5M1|$sVYSULAa>%=0KdEjX1xIZA5POa@SmN`bM`Dq$)dE-G%jpr0lu!IE9E?zzRzz|&}JQm z>eCX2Pu>K^2fWZR@*P7Rd=JSfAwT%ZO)21mKBLTQAx=_4x=#wo;A`PvK$474zHM^A ze}EZnVYOE%739KWK=s+V$_>yI!so(+80z2yVNI6=@cpNz`FK?Vzv#-#d{7hN zt0(twI*3PiS3OIK@uJ;T2T|zm>W?dkQP}RPgNSr@HR%IlM3%eiARgUaotjOJ7wxV( zh(vc+w{N6HVz{dgBGTQ}id%>g+3u=?h;(;#_I6@Kio5C{6y05Y=AYzHWOvm;D7w2^ z^D}BF++B4LhwiS*yNGef?y7@Gba(ZIJ;X?Kca<8=cr1eVSGv1;buSjJ$i|||8n+U- z@hIJ^L7$yq(^0Pk?ymOPhshG$RVTX+s=GRBKNi5^u2M5M3WD#y(M>_+1DLSsu2Ofs zWLM6>n;I3nhbgrbys7-M&5X1A{t)?nV+&o zy{)(DSvYj8fg(FT5EDEe#C<^uR5UE9+D0d20(TY26#YT1&|5{?yz#hLz&@Ec0;tiy z+f2!eWXd--eaj$CZUo`!>>Jux!z1 z)%y>c1wbs($akCh>{>u|`gfaS80y9NZqp_Qh~6n0E#NPxtC##GrG=)e2azz0&Up^g zM=O{`(~o>JNnrh0T|KBy|7P+WLtXR-I6ri~R9)ja@F}6!uX2h2P6dPi0zS8s)S#^M zg=54f2b?Q|wlMQ6N(JSxw@tvcSX&6yOJAz6A`A2lyimvHAVXc^z<0D_$PYdu)B-Xu z0LB6@Ybr1?6ma0%ALIi*Su{K^n;fPv`|#UClom$5nY_*(AyA!uNS{AUS1-mPeVZKc zB?RrmW3(LPuzPQ_ia_ZYU4EhgVXdgcKHKm0>J;eF^ekH(*nyf1RfC9e~6*NH) zNq$igWbipG=vKpZv&n%y50xX793o$Z+ROo{PX8)&4MV*cUxnJ_z+TB7I!bAwX?jps zvdW;(Vm)x2*2~~>!KV`*@TwJa^1M3i>pVCVRR=f58c)K5|4qq zdN%dJiyztJ8QYxD39LS-t5-~Ibik~~wH*&|m^0ngEBOtUbCj!B%(PKgujIFwyv5a{ z3Y$GiclElEg~_g-gGh8&FK~v~1iGu|AR67(TYZ)ojpgb&h(~wzZk#8^i+1%KM4`KS z7k(s0VY_+`BGO&GS_Q<2ELYD#Ji4nl=`uB5w5#VJ65ZAN_$Dn9!_{*Tk?!i#JVyzwnI07}S%RzQ zWYjD=Xo)JMx>$Cr&i9jW=vV_qc6`RrDFvXz$d0(M1B!+vanzIn Date: Fri, 11 Jul 2025 08:58:27 +0800 Subject: [PATCH 53/53] Fix a bug in gitdiff2yaml Signed-off-by: Jiang, Zhiwei --- clang/lib/DPCT/IncMigration/ReMigration.cpp | 4 +- clang/tools/gitdiff2yaml/src/gitdiff2yaml.cpp | 199 +++++++++--------- clang/tools/gitdiff2yaml/src/gitdiff2yaml.h | 8 - clang/tools/gitdiff2yaml/src/main.cpp | 24 ++- 4 files changed, 129 insertions(+), 106 deletions(-) diff --git a/clang/lib/DPCT/IncMigration/ReMigration.cpp b/clang/lib/DPCT/IncMigration/ReMigration.cpp index 463c404cd8c9..9ea94180b420 100644 --- a/clang/lib/DPCT/IncMigration/ReMigration.cpp +++ b/clang/lib/DPCT/IncMigration/ReMigration.cpp @@ -672,8 +672,8 @@ std::vector mergeC1AndC2( // [SYCL code 2.1] // // Repl_A: Read from gitdiff2yaml generated files. -// Repl_B: Curent in-memory migration replacements. -// Repl C1: Read from MainSourceFiles.yaml (and *.h.yaml) file(s). +// Repl_B: Current in-memory migration replacements. +// Repl C1: Read from MainSourceFiles.yaml file. // Repl_C2: Read from gitdiff2yaml generated files. // // Repl_A has 4 parts: diff --git a/clang/tools/gitdiff2yaml/src/gitdiff2yaml.cpp b/clang/tools/gitdiff2yaml/src/gitdiff2yaml.cpp index c0ec70ff73e9..b7fe1e65ca6f 100644 --- a/clang/tools/gitdiff2yaml/src/gitdiff2yaml.cpp +++ b/clang/tools/gitdiff2yaml/src/gitdiff2yaml.cpp @@ -29,6 +29,14 @@ bool startsWith(const std::string &Str, const std::string &Prefix) { Str.compare(0, Prefix.size(), Prefix) == 0; } +struct HunkContext { + unsigned OldCurrentLine = 0; + bool InHunk = false; + bool FastForward = false; + std::string CurrentNewFilePath; + std::string CurrentOldFilePath; +}; + bool parseHunkHeader(const std::string &Line, HunkContext &HC) { const std::string HunkHeaderPrefix = "@@ -"; if (!startsWith(Line, HunkHeaderPrefix)) @@ -38,14 +46,19 @@ bool parseHunkHeader(const std::string &Line, HunkContext &HC) { // @@ -0,0 +1,3 @@ // ^ // |-- OldEnd + // E.g., + // @@ -24 +24 @@ + // ^ + // |-- OldEnd size_t OldEnd = Line.find(' ', HunkHeaderPrefix.size()); std::string OldPart = Line.substr(HunkHeaderPrefix.size(), OldEnd - HunkHeaderPrefix.size()); size_t Comma = OldPart.find(','); if (Comma == std::string::npos) { - throw std::runtime_error("Invalid hunk header format: " + Line); + HC.OldCurrentLine = std::stoi(OldPart); + } else { + HC.OldCurrentLine = std::stoi(OldPart.substr(0, Comma)); } - HC.OldCurrentLine = std::stoi(OldPart.substr(0, Comma)); return true; } @@ -70,98 +83,6 @@ std::vector calculateOldOffset(const std::string &OldFileContent) { return Ret; } -// 1. Assume the line ending in the file is '\n'. -// 2. The pair (---, +++) may occurs multiple times in one hunk, so we use a -// variable to save the delete (-) operation. The continuous delete operations -// are treated as one operation. -// 3. After the delete operation, if the next line is one or more '+' -// operations, we make them as a replace-replacement. If the next line is a -// context line, the delete operation is a delete-replacement. Then clear the -// variable. -// 4. If we meet insertions ('+') when the variable is empty, we treat it as an -// insert-replacement. -void processHunkBody(const std::string &Line, HunkContext &Ctx, - std::vector &Repls, - const std::vector &CurrentOldFileOffset) { - static std::optional< - std::pair> - DeleteInfo; - static std::optional< - std::pair> - AddInfo; - - auto addRepl = [&]() { - Replacement R; - if (DeleteInfo.has_value() && AddInfo.has_value()) { - // replace-replacement - R.OldFilePath = Ctx.CurrentOldFilePath; - R.NewFilePath = Ctx.CurrentNewFilePath; - R.Length = DeleteInfo->second; - R.Offset = CurrentOldFileOffset[DeleteInfo->first]; - R.ReplacementText = AddInfo->second; - DeleteInfo.reset(); - AddInfo.reset(); - } else if (DeleteInfo.has_value()) { - // delete-replacement - R.OldFilePath = Ctx.CurrentOldFilePath; - R.NewFilePath = Ctx.CurrentNewFilePath; - R.Length = DeleteInfo->second; - R.Offset = CurrentOldFileOffset[DeleteInfo->first]; - R.ReplacementText = ""; - DeleteInfo.reset(); - } else if (AddInfo.has_value()) { - // insert-replacement - R.OldFilePath = Ctx.CurrentOldFilePath; - R.NewFilePath = Ctx.CurrentNewFilePath; - R.Length = 0; - R.Offset = CurrentOldFileOffset[AddInfo->first]; - R.ReplacementText = AddInfo->second; - AddInfo.reset(); - } - Repls.push_back(R); - }; - - // Hunk end - if (Line.empty()) { - addRepl(); - Ctx.InHunk = false; - return; - } - - switch (Line[0]) { - case ' ': { - addRepl(); - Ctx.OldCurrentLine++; - break; - } - case '-': { - if (!DeleteInfo.has_value()) { - auto Item = std::pair( - Ctx.OldCurrentLine, - Line.length()); // +1 for the newline character, -1 for the - // '-' in the line beginng - DeleteInfo = Item; - } else { - DeleteInfo->second += - (Line.length()); // +1 for the newline character, -1 for the - // '-' in the line beginng - } - Ctx.OldCurrentLine++; - break; - } - case '+': { - if (!AddInfo.has_value()) { - auto Item = std::pair(Ctx.OldCurrentLine, - Line.substr(1) + LineEnd); - AddInfo = Item; - } else { - AddInfo->second += (Line.substr(1) + LineEnd); - } - break; - } - } -} - struct ModifyHunk { ModifyHunk() = default; ModifyHunk(std::string FilePath, unsigned Offset, unsigned Length, @@ -356,11 +277,51 @@ std::vector parseDiff(const std::string &diffOutput, HunkContext HC; std::vector CurrentOldFileOffset; + std::optional< + std::pair> + DeleteInfo; + std::optional> + AddInfo; + + auto addRepl = [&]() { + Replacement R; + if (DeleteInfo.has_value() && AddInfo.has_value()) { + // replace-replacement + R.OldFilePath = HC.CurrentOldFilePath; + R.NewFilePath = HC.CurrentNewFilePath; + R.Length = DeleteInfo->second; + R.Offset = CurrentOldFileOffset[DeleteInfo->first]; + R.ReplacementText = AddInfo->second; + DeleteInfo.reset(); + AddInfo.reset(); + } else if (DeleteInfo.has_value()) { + // delete-replacement + R.OldFilePath = HC.CurrentOldFilePath; + R.NewFilePath = HC.CurrentNewFilePath; + R.Length = DeleteInfo->second; + R.Offset = CurrentOldFileOffset[DeleteInfo->first]; + R.ReplacementText = ""; + DeleteInfo.reset(); + } else if (AddInfo.has_value()) { + // insert-replacement + R.OldFilePath = HC.CurrentOldFilePath; + R.NewFilePath = HC.CurrentNewFilePath; + R.Length = 0; + R.Offset = CurrentOldFileOffset[AddInfo->first]; + R.ReplacementText = AddInfo->second; + AddInfo.reset(); + } + replacements.push_back(R); + }; + // Don't use std::getline as condition of the while loop, because it will // return false if the last line only containing EOF. while (iss.good()) { std::getline(iss, line); if (startsWith(line, "diff --git")) { + if (HC.InHunk) + addRepl(); + HC.InHunk = false; HC.FastForward = false; continue; } @@ -368,6 +329,9 @@ std::vector parseDiff(const std::string &diffOutput, continue; if (startsWith(line, "---")) { + if (HC.InHunk) + addRepl(); + HC.InHunk = false; HC.CurrentOldFilePath = line.substr(4) == "/dev/null" ? "/dev/null" : line.substr(6); if (HC.CurrentOldFilePath != "/dev/null") { @@ -383,6 +347,9 @@ std::vector parseDiff(const std::string &diffOutput, continue; } if (startsWith(line, "+++")) { + if (HC.InHunk) + addRepl(); + HC.InHunk = false; HC.CurrentNewFilePath = line.substr(4) == "/dev/null" ? "/dev/null" : line.substr(6); if (HC.CurrentOldFilePath == "/dev/null" || @@ -397,16 +364,58 @@ std::vector parseDiff(const std::string &diffOutput, } if (parseHunkHeader(line, HC)) { + if (HC.InHunk) + addRepl(); // Hunk start HC.InHunk = true; continue; } + // parse hunk body + // 1. Assume the line ending in the file is '\n'. + // 2. The pair (---, +++) should occur only once in one hunk, since + // --unified=0 + // 3. We use a variable to save the delete (-) operation. The continuous + // delete operations are treated as one operation. + // 4. After the delete operation, if the next line is one or more '+' + // operations, we make them as a replace-replacement. If the next line is a + // context line, the delete operation is a delete-replacement. Then clear + // the variable. + // 5. If we meet insertions ('+') when the variable is empty, we treat it as + // an insert-replacement. if (HC.InHunk) { - processHunkBody(line, HC, replacements, CurrentOldFileOffset); + switch (line[0]) { + case '-': { + if (!DeleteInfo.has_value()) { + auto Item = std::pair( + HC.OldCurrentLine, + line.length()); // +1 for the newline character, -1 for the + // '-' in the line beginng + DeleteInfo = Item; + } else { + DeleteInfo->second += + (line.length()); // +1 for the newline character, -1 for the + // '-' in the line beginng + } + HC.OldCurrentLine++; + break; + } + case '+': { + if (!AddInfo.has_value()) { + auto Item = std::pair( + HC.OldCurrentLine, line.substr(1) + LineEnd); + AddInfo = Item; + } else { + AddInfo->second += (line.substr(1) + LineEnd); + } + break; + } + } continue; } } + if (HC.InHunk) + addRepl(); return replacements; } diff --git a/clang/tools/gitdiff2yaml/src/gitdiff2yaml.h b/clang/tools/gitdiff2yaml/src/gitdiff2yaml.h index cc4dfa8de5b2..b22f930b87bb 100644 --- a/clang/tools/gitdiff2yaml/src/gitdiff2yaml.h +++ b/clang/tools/gitdiff2yaml/src/gitdiff2yaml.h @@ -22,14 +22,6 @@ struct Replacement { std::string ReplacementText; }; -struct HunkContext { - unsigned OldCurrentLine = 0; - bool InHunk = false; - bool FastForward = false; - std::string CurrentNewFilePath; - std::string CurrentOldFilePath; -}; - std::string execGitCommand(const std::string &CMD); std::vector parseDiff(const std::string &diffOutput, const std::string &RepoRoot); diff --git a/clang/tools/gitdiff2yaml/src/main.cpp b/clang/tools/gitdiff2yaml/src/main.cpp index f441af43f248..2f8d1bc25870 100644 --- a/clang/tools/gitdiff2yaml/src/main.cpp +++ b/clang/tools/gitdiff2yaml/src/main.cpp @@ -17,6 +17,7 @@ #include "gitdiff2yaml.h" #include "llvm/Support/CommandLine.h" +#include "llvm/Support/Path.h" #include #include @@ -69,7 +70,9 @@ int main(int argc, char *argv[]) { RepoRoot = RepoRoot.substr(0, RepoRoot.size() - 1); // Remove the last '\n' std::string NewCommitID = execGitCommand("git log -1 --format=\"%H\""); - std::string DiffOutput = execGitCommand("git diff " + OldCommitID.getValue()); + std::string DiffOutput = + execGitCommand("git diff --diff-algorithm=minimal --unified=0 " + + OldCommitID.getValue()); execGitCommand("git reset --hard " + OldCommitID.getValue()); std::vector Repls = parseDiff(DiffOutput, RepoRoot); @@ -84,6 +87,25 @@ int main(int argc, char *argv[]) { }), Repls.end()); + // Erase unrelated replacements + Repls.erase(std::remove_if( + Repls.begin(), Repls.end(), + [](Replacement x) { + if (x.NewFilePath == "/dev/null") + return false; + llvm::StringRef PathRef = x.NewFilePath; + std::string Ext = + llvm::sys::path::extension(PathRef).substr(1).lower(); + if (Ext == "cu" || Ext == "cuh" || Ext == "cpp" || + Ext == "hpp" || Ext == "cxx" || Ext == "hxx" || + Ext == "cc" || Ext == "hh" || Ext == "c" || + Ext == "h") { + return false; + } + return true; + }), + Repls.end()); + if (!OutputFilename.empty()) { std::ofstream OutFile(OutputFilename.getValue()); if (!OutFile.is_open()) {