| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,112 @@ | ||
| """ | ||
| Make sure that the concurrent vfork() from multiple threads works correctly. | ||
| """ | ||
|
|
||
| import lldb | ||
| import lldbsuite.test.lldbutil as lldbutil | ||
| from lldbsuite.test.lldbtest import * | ||
| from lldbsuite.test.decorators import * | ||
|
|
||
|
|
||
| class TestConcurrentVFork(TestBase): | ||
| NO_DEBUG_INFO_TESTCASE = True | ||
|
|
||
| def build_run_to_breakpoint(self, use_fork, call_exec): | ||
| self.build() | ||
|
|
||
| args = [] | ||
| if use_fork: | ||
| args.append("--fork") | ||
| if call_exec: | ||
| args.append("--exec") | ||
| launch_info = lldb.SBLaunchInfo(args) | ||
| launch_info.SetWorkingDirectory(self.getBuildDir()) | ||
|
|
||
| return lldbutil.run_to_source_breakpoint( | ||
| self, "// break here", lldb.SBFileSpec("main.cpp") | ||
| ) | ||
|
|
||
| def follow_parent_helper(self, use_fork, call_exec): | ||
| (target, process, thread, bkpt) = self.build_run_to_breakpoint( | ||
| use_fork, call_exec | ||
| ) | ||
|
|
||
| parent_pid = target.FindFirstGlobalVariable("g_pid").GetValueAsUnsigned() | ||
| self.runCmd("settings set target.process.follow-fork-mode parent") | ||
| self.runCmd("settings set target.process.stop-on-exec False", check=False) | ||
| self.expect( | ||
| "continue", substrs=[f"Process {parent_pid} exited with status = 0"] | ||
| ) | ||
|
|
||
| def follow_child_helper(self, use_fork, call_exec): | ||
| self.build_run_to_breakpoint(use_fork, call_exec) | ||
|
|
||
| self.runCmd("settings set target.process.follow-fork-mode child") | ||
| self.runCmd("settings set target.process.stop-on-exec False", check=False) | ||
| # Child process exits with code "index + 10" since index is [0-4] | ||
| # so the exit code should be 1[0-4] | ||
| self.expect("continue", patterns=[r"exited with status = 1[0-4]"]) | ||
|
|
||
| @skipUnlessPlatform(["linux"]) | ||
| def test_follow_parent_vfork_no_exec(self): | ||
| """ | ||
| Make sure that debugging concurrent vfork() from multiple threads won't crash lldb during follow-parent. | ||
| And follow-parent successfully detach all child processes and exit debugger without calling exec. | ||
| """ | ||
| self.follow_parent_helper(use_fork=False, call_exec=False) | ||
|
|
||
| @skipUnlessPlatform(["linux"]) | ||
| def test_follow_parent_fork_no_exec(self): | ||
| """ | ||
| Make sure that debugging concurrent fork() from multiple threads won't crash lldb during follow-parent. | ||
| And follow-parent successfully detach all child processes and exit debugger without calling exec | ||
| """ | ||
| self.follow_parent_helper(use_fork=True, call_exec=False) | ||
|
|
||
| @skipUnlessPlatform(["linux"]) | ||
| def test_follow_parent_vfork_call_exec(self): | ||
| """ | ||
| Make sure that debugging concurrent vfork() from multiple threads won't crash lldb during follow-parent. | ||
| And follow-parent successfully detach all child processes and exit debugger after calling exec. | ||
| """ | ||
| self.follow_parent_helper(use_fork=False, call_exec=True) | ||
|
|
||
| @skipUnlessPlatform(["linux"]) | ||
| def test_follow_parent_fork_call_exec(self): | ||
| """ | ||
| Make sure that debugging concurrent vfork() from multiple threads won't crash lldb during follow-parent. | ||
| And follow-parent successfully detach all child processes and exit debugger after calling exec. | ||
| """ | ||
| self.follow_parent_helper(use_fork=True, call_exec=True) | ||
|
|
||
| @skipUnlessPlatform(["linux"]) | ||
| def test_follow_child_vfork_no_exec(self): | ||
| """ | ||
| Make sure that debugging concurrent vfork() from multiple threads won't crash lldb during follow-child. | ||
| And follow-child successfully detach parent process and exit child process with correct exit code without calling exec. | ||
| """ | ||
| self.follow_child_helper(use_fork=False, call_exec=False) | ||
|
|
||
| @skipUnlessPlatform(["linux"]) | ||
| def test_follow_child_fork_no_exec(self): | ||
| """ | ||
| Make sure that debugging concurrent fork() from multiple threads won't crash lldb during follow-child. | ||
| And follow-child successfully detach parent process and exit child process with correct exit code without calling exec. | ||
| """ | ||
| self.follow_child_helper(use_fork=True, call_exec=False) | ||
|
|
||
| @skipUnlessPlatform(["linux"]) | ||
| def test_follow_child_vfork_call_exec(self): | ||
| """ | ||
| Make sure that debugging concurrent vfork() from multiple threads won't crash lldb during follow-child. | ||
| And follow-child successfully detach parent process and exit child process with correct exit code after calling exec. | ||
| """ | ||
| self.follow_child_helper(use_fork=False, call_exec=True) | ||
|
|
||
| @skipUnlessPlatform(["linux"]) | ||
| def test_follow_child_fork_call_exec(self): | ||
| """ | ||
| Make sure that debugging concurrent fork() from multiple threads won't crash lldb during follow-child. | ||
| And follow-child successfully detach parent process and exit child process with correct exit code after calling exec. | ||
| """ | ||
| self.follow_child_helper(use_fork=True, call_exec=True) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,105 @@ | ||
| #include <assert.h> | ||
| #include <iostream> | ||
| #include <mutex> | ||
| #include <sys/wait.h> | ||
| #include <thread> | ||
| #include <unistd.h> | ||
| #include <vector> | ||
|
|
||
| pid_t g_pid = 0; | ||
| std::mutex g_child_pids_mutex; | ||
| std::vector<pid_t> g_child_pids; | ||
|
|
||
| const char *g_program = nullptr; | ||
| bool g_use_vfork = true; // Use vfork by default. | ||
| bool g_call_exec = false; // Does not call exec by default. | ||
|
|
||
| int call_vfork(int index) { | ||
| pid_t child_pid = 0; | ||
| if (g_use_vfork) { | ||
| child_pid = vfork(); | ||
| } else { | ||
| child_pid = fork(); | ||
| } | ||
|
|
||
| if (child_pid == -1) { | ||
| // Error handling | ||
| perror("vfork"); | ||
| return 1; | ||
| } else if (child_pid == 0) { | ||
| // This code is executed by the child process | ||
| g_pid = getpid(); | ||
| printf("Child process: %d\n", g_pid); | ||
|
|
||
| if (g_call_exec) { | ||
| std::string child_exit_code = std::to_string(index + 10); | ||
| execl(g_program, g_program, "--child", child_exit_code.c_str(), NULL); | ||
| } else { | ||
| _exit(index + 10); | ||
| } | ||
| } else { | ||
| // This code is executed by the parent process | ||
| printf("[Parent] Forked process id: %d\n", child_pid); | ||
| } | ||
| return 0; | ||
| } | ||
|
|
||
| void wait_all_children_to_exit() { | ||
| std::lock_guard<std::mutex> Lock(g_child_pids_mutex); | ||
| for (pid_t child_pid : g_child_pids) { | ||
| int child_status = 0; | ||
| pid_t pid = waitpid(child_pid, &child_status, 0); | ||
| if (child_status != 0) { | ||
| int exit_code = WEXITSTATUS(child_status); | ||
| if (exit_code > 15 || exit_code < 10) { | ||
| printf("Error: child process exits with unexpected code %d\n", | ||
| exit_code); | ||
| _exit(1); // This will let our program know that some child processes | ||
| // didn't exist with an expected exit status. | ||
| } | ||
| } | ||
| if (pid != child_pid) | ||
| _exit(2); // This will let our program know it didn't succeed | ||
| } | ||
| } | ||
|
|
||
| void create_threads(int num_threads) { | ||
| std::vector<std::thread> threads; | ||
| for (int i = 0; i < num_threads; ++i) { | ||
| threads.emplace_back(std::thread(call_vfork, i)); | ||
| } | ||
| printf("Created %d threads, joining...\n", | ||
| num_threads); // end_of_create_threads | ||
| for (auto &thread : threads) { | ||
| thread.join(); | ||
| } | ||
| wait_all_children_to_exit(); | ||
| } | ||
|
|
||
| // Can be called in various ways: | ||
| // 1. [program]: use vfork and not call exec | ||
| // 2. [program] --fork: use fork and not call exec | ||
| // 3. [program] --fork --exec: use fork and call exec | ||
| // 4. [program] --exec: use vfork and call exec | ||
| // 5. [program] --child [exit_code]: child process | ||
| int main(int argc, char *argv[]) { | ||
| g_pid = getpid(); | ||
| g_program = argv[0]; | ||
|
|
||
| for (int i = 1; i < argc; ++i) { | ||
| if (strcmp(argv[i], "--child") == 0) { | ||
| assert(i + 1 < argc); | ||
| int child_exit_code = std::stoi(argv[i + 1]); | ||
| printf("Child process: %d, exiting with code %d\n", g_pid, | ||
| child_exit_code); | ||
| _exit(child_exit_code); | ||
| } else if (strcmp(argv[i], "--fork") == 0) | ||
| g_use_vfork = false; | ||
| else if (strcmp(argv[i], "--exec") == 0) | ||
| g_call_exec = true; | ||
| } | ||
|
|
||
| int num_threads = 5; // break here | ||
| create_threads(num_threads); | ||
| return 0; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| C_SOURCES := main.c | ||
|
|
||
| include Makefile.rules |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,134 @@ | ||
| """Test Python APIs for setting, getting, and using address masks.""" | ||
|
|
||
| import os | ||
| import lldb | ||
| from lldbsuite.test.decorators import * | ||
| from lldbsuite.test.lldbtest import * | ||
| from lldbsuite.test import lldbutil | ||
|
|
||
|
|
||
| class AddressMasksTestCase(TestBase): | ||
| NO_DEBUG_INFO_TESTCASE = True | ||
|
|
||
| def reset_all_masks(self, process): | ||
| process.SetAddressMask( | ||
| lldb.eAddressMaskTypeAll, | ||
| lldb.LLDB_INVALID_ADDRESS_MASK, | ||
| lldb.eAddressMaskRangeAll, | ||
| ) | ||
| self.runCmd("settings set target.process.virtual-addressable-bits 0") | ||
| self.runCmd("settings set target.process.highmem-virtual-addressable-bits 0") | ||
|
|
||
| @skipIf(archs=["arm"]) # 32-bit arm ABI hardcodes Code mask, is 32-bit | ||
| def test_address_masks(self): | ||
| self.build() | ||
| (target, process, t, bp) = lldbutil.run_to_source_breakpoint( | ||
| self, "break here", lldb.SBFileSpec("main.c") | ||
| ) | ||
|
|
||
| process.SetAddressableBits(lldb.eAddressMaskTypeAll, 42) | ||
| self.assertEqual(0x0000029500003F94, process.FixAddress(0x00265E9500003F94)) | ||
| self.reset_all_masks(process) | ||
|
|
||
| # ~((1ULL<<42)-1) == 0xfffffc0000000000 | ||
| process.SetAddressMask(lldb.eAddressMaskTypeAll, 0xFFFFFC0000000000) | ||
| self.assertEqual(0x0000029500003F94, process.FixAddress(0x00265E9500003F94)) | ||
| self.reset_all_masks(process) | ||
|
|
||
| # Check that all bits can pass through unmodified | ||
| process.SetAddressableBits(lldb.eAddressMaskTypeAll, 64) | ||
| self.assertEqual(0x00265E9500003F94, process.FixAddress(0x00265E9500003F94)) | ||
| self.reset_all_masks(process) | ||
|
|
||
| process.SetAddressableBits( | ||
| lldb.eAddressMaskTypeAll, 42, lldb.eAddressMaskRangeAll | ||
| ) | ||
| self.assertEqual(0x000002950001F694, process.FixAddress(0x00265E950001F694)) | ||
| self.assertEqual(0xFFFFFE950000F694, process.FixAddress(0xFFA65E950000F694)) | ||
| self.reset_all_masks(process) | ||
|
|
||
| # Set a eAddressMaskTypeCode which has the low 3 bits marked as non-address | ||
| # bits, confirm that they're cleared by FixAddress. | ||
| process.SetAddressableBits( | ||
| lldb.eAddressMaskTypeAll, 42, lldb.eAddressMaskRangeAll | ||
| ) | ||
| mask = process.GetAddressMask(lldb.eAddressMaskTypeAny) | ||
| process.SetAddressMask(lldb.eAddressMaskTypeCode, mask | 0x3) | ||
| self.assertEqual(0x000002950001F697, process.FixAddress(0x00265E950001F697)) | ||
| self.assertEqual(0xFFFFFE950000F697, process.FixAddress(0xFFA65E950000F697)) | ||
| self.assertEqual( | ||
| 0x000002950001F697, | ||
| process.FixAddress(0x00265E950001F697, lldb.eAddressMaskTypeData), | ||
| ) | ||
| self.assertEqual( | ||
| 0x000002950001F694, | ||
| process.FixAddress(0x00265E950001F697, lldb.eAddressMaskTypeCode), | ||
| ) | ||
| self.reset_all_masks(process) | ||
|
|
||
| # The user can override whatever settings the Process thinks should be used. | ||
| process.SetAddressableBits( | ||
| lldb.eAddressMaskTypeAll, 42, lldb.eAddressMaskRangeLow | ||
| ) | ||
| self.runCmd("settings set target.process.virtual-addressable-bits 15") | ||
| self.assertEqual(0x0000000000007694, process.FixAddress(0x00265E950001F694)) | ||
| self.assertEqual(0xFFFFFFFFFFFFF694, process.FixAddress(0xFFA65E950000F694)) | ||
| self.runCmd("settings set target.process.virtual-addressable-bits 0") | ||
| self.assertEqual(0x000002950001F694, process.FixAddress(0x00265E950001F694)) | ||
| self.reset_all_masks(process) | ||
|
|
||
| # AArch64 can have different address masks for high and low memory, when different | ||
| # page tables are set up. | ||
| @skipIf(archs=no_match(["arm64", "arm64e", "aarch64"])) | ||
| @skipIf(archs=["arm"]) # 32-bit arm ABI hardcodes Code mask, is 32-bit | ||
| def test_address_masks_target_supports_highmem_tests(self): | ||
| self.build() | ||
| (target, process, t, bp) = lldbutil.run_to_source_breakpoint( | ||
| self, "break here", lldb.SBFileSpec("main.c") | ||
| ) | ||
|
|
||
| process.SetAddressableBits( | ||
| lldb.eAddressMaskTypeAll, 42, lldb.eAddressMaskRangeLow | ||
| ) | ||
| process.SetAddressableBits( | ||
| lldb.eAddressMaskTypeAll, 15, lldb.eAddressMaskRangeHigh | ||
| ) | ||
| self.assertEqual(0x000002950001F694, process.FixAddress(0x00265E950001F694)) | ||
| self.assertEqual(0xFFFFFFFFFFFFF694, process.FixAddress(0xFFA65E950000F694)) | ||
| self.reset_all_masks(process) | ||
|
|
||
| # The user can override whatever settings the Process thinks should be used. | ||
| process.SetAddressableBits( | ||
| lldb.eAddressMaskTypeAll, 42, lldb.eAddressMaskRangeAll | ||
| ) | ||
| self.runCmd("settings set target.process.virtual-addressable-bits 15") | ||
| self.runCmd("settings set target.process.highmem-virtual-addressable-bits 15") | ||
| self.assertEqual(0x0000000000007694, process.FixAddress(0x00265E950001F694)) | ||
| self.assertEqual(0xFFFFFFFFFFFFF694, process.FixAddress(0xFFA65E950000F694)) | ||
| self.runCmd("settings set target.process.virtual-addressable-bits 0") | ||
| self.runCmd("settings set target.process.highmem-virtual-addressable-bits 0") | ||
| self.assertEqual(0x000002950001F694, process.FixAddress(0x00265E950001F694)) | ||
| self.reset_all_masks(process) | ||
|
|
||
| # On most targets where we have a single mask for all address range, confirm | ||
| # that the high memory masks are ignored. | ||
| @skipIf(archs=["arm64", "arm64e", "aarch64"]) | ||
| @skipIf(archs=["arm"]) # 32-bit arm ABI hardcodes Code mask, is 32-bit | ||
| def test_address_masks_target_no_highmem(self): | ||
| self.build() | ||
| (target, process, t, bp) = lldbutil.run_to_source_breakpoint( | ||
| self, "break here", lldb.SBFileSpec("main.c") | ||
| ) | ||
|
|
||
| process.SetAddressableBits( | ||
| lldb.eAddressMaskTypeAll, 42, lldb.eAddressMaskRangeLow | ||
| ) | ||
| process.SetAddressableBits( | ||
| lldb.eAddressMaskTypeAll, 15, lldb.eAddressMaskRangeHigh | ||
| ) | ||
| self.assertEqual(0x000002950001F694, process.FixAddress(0x00265E950001F694)) | ||
| self.assertEqual(0xFFFFFE950000F694, process.FixAddress(0xFFA65E950000F694)) | ||
| self.runCmd("settings set target.process.virtual-addressable-bits 15") | ||
| self.runCmd("settings set target.process.highmem-virtual-addressable-bits 42") | ||
| self.assertEqual(0x0000000000007694, process.FixAddress(0x00265E950001F694)) | ||
| self.assertEqual(0xFFFFFFFFFFFFF694, process.FixAddress(0xFFA65E950000F694)) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| #include <stdio.h> | ||
|
|
||
| int main(int argc, char const *argv[]) { | ||
| puts("Hello address masking world"); // break here | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,94 @@ | ||
| //===- AMDGPUMCExpr.cpp - AMDGPU specific MC expression classes -----------===// | ||
| // | ||
| // 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 "AMDGPUMCExpr.h" | ||
| #include "llvm/MC/MCContext.h" | ||
| #include "llvm/MC/MCStreamer.h" | ||
| #include "llvm/MC/MCSymbol.h" | ||
| #include "llvm/MC/MCValue.h" | ||
| #include "llvm/Support/Allocator.h" | ||
| #include "llvm/Support/raw_ostream.h" | ||
| #include <optional> | ||
|
|
||
| using namespace llvm; | ||
|
|
||
| const AMDGPUVariadicMCExpr * | ||
| AMDGPUVariadicMCExpr::create(VariadicKind Kind, ArrayRef<const MCExpr *> Args, | ||
| MCContext &Ctx) { | ||
| return new (Ctx) AMDGPUVariadicMCExpr(Kind, Args); | ||
| } | ||
|
|
||
| const MCExpr *AMDGPUVariadicMCExpr::getSubExpr(size_t Index) const { | ||
| assert(Index < Args.size() && | ||
| "Indexing out of bounds AMDGPUVariadicMCExpr sub-expr"); | ||
| return Args[Index]; | ||
| } | ||
|
|
||
| void AMDGPUVariadicMCExpr::printImpl(raw_ostream &OS, | ||
| const MCAsmInfo *MAI) const { | ||
| switch (Kind) { | ||
| default: | ||
| llvm_unreachable("Unknown AMDGPUVariadicMCExpr kind."); | ||
| case AGVK_Or: | ||
| OS << "or("; | ||
| break; | ||
| case AGVK_Max: | ||
| OS << "max("; | ||
| break; | ||
| } | ||
| for (auto It = Args.begin(); It != Args.end(); ++It) { | ||
| (*It)->print(OS, MAI, /*InParens=*/false); | ||
| if ((It + 1) != Args.end()) | ||
| OS << ", "; | ||
| } | ||
| OS << ')'; | ||
| } | ||
|
|
||
| static int64_t op(AMDGPUVariadicMCExpr::VariadicKind Kind, int64_t Arg1, | ||
| int64_t Arg2) { | ||
| switch (Kind) { | ||
| default: | ||
| llvm_unreachable("Unknown AMDGPUVariadicMCExpr kind."); | ||
| case AMDGPUVariadicMCExpr::AGVK_Max: | ||
| return std::max(Arg1, Arg2); | ||
| case AMDGPUVariadicMCExpr::AGVK_Or: | ||
| return Arg1 | Arg2; | ||
| } | ||
| } | ||
|
|
||
| bool AMDGPUVariadicMCExpr::evaluateAsRelocatableImpl( | ||
| MCValue &Res, const MCAsmLayout *Layout, const MCFixup *Fixup) const { | ||
| std::optional<int64_t> Total; | ||
|
|
||
| for (const MCExpr *Arg : Args) { | ||
| MCValue ArgRes; | ||
| if (!Arg->evaluateAsRelocatable(ArgRes, Layout, Fixup) || | ||
| !ArgRes.isAbsolute()) | ||
| return false; | ||
|
|
||
| if (!Total.has_value()) | ||
| Total = ArgRes.getConstant(); | ||
| Total = op(Kind, *Total, ArgRes.getConstant()); | ||
| } | ||
|
|
||
| Res = MCValue::get(*Total); | ||
| return true; | ||
| } | ||
|
|
||
| void AMDGPUVariadicMCExpr::visitUsedExpr(MCStreamer &Streamer) const { | ||
| for (const MCExpr *Arg : Args) | ||
| Streamer.visitUsedExpr(*Arg); | ||
| } | ||
|
|
||
| MCFragment *AMDGPUVariadicMCExpr::findAssociatedFragment() const { | ||
| for (const MCExpr *Arg : Args) { | ||
| if (Arg->findAssociatedFragment()) | ||
| return Arg->findAssociatedFragment(); | ||
| } | ||
| return nullptr; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,74 @@ | ||
| //===- AMDGPUMCExpr.h - AMDGPU specific MC expression classes ---*- 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 LLVM_LIB_TARGET_AMDGPU_MCTARGETDESC_AMDGPUMCEXPR_H | ||
| #define LLVM_LIB_TARGET_AMDGPU_MCTARGETDESC_AMDGPUMCEXPR_H | ||
|
|
||
| #include "llvm/ADT/ArrayRef.h" | ||
| #include "llvm/ADT/SmallVector.h" | ||
| #include "llvm/MC/MCExpr.h" | ||
|
|
||
| namespace llvm { | ||
|
|
||
| /// AMDGPU target specific variadic MCExpr operations. | ||
| /// | ||
| /// Takes in a minimum of 1 argument to be used with an operation. The supported | ||
| /// operations are: | ||
| /// - (bitwise) or | ||
| /// - max | ||
| /// | ||
| /// \note If the 'or'/'max' operations are provided only a single argument, the | ||
| /// operation will act as a no-op and simply resolve as the provided argument. | ||
| /// | ||
| class AMDGPUVariadicMCExpr : public MCTargetExpr { | ||
| public: | ||
| enum VariadicKind { AGVK_None, AGVK_Or, AGVK_Max }; | ||
|
|
||
| private: | ||
| VariadicKind Kind; | ||
| SmallVector<const MCExpr *, 2> Args; | ||
|
|
||
| AMDGPUVariadicMCExpr(VariadicKind Kind, ArrayRef<const MCExpr *> Args) | ||
| : Kind(Kind), Args(Args) { | ||
| assert(Args.size() >= 1 && "Needs a minimum of one expression."); | ||
| assert(Kind != AGVK_None && | ||
| "Cannot construct AMDGPUVariadicMCExpr of kind none."); | ||
| } | ||
|
|
||
| public: | ||
| static const AMDGPUVariadicMCExpr * | ||
| create(VariadicKind Kind, ArrayRef<const MCExpr *> Args, MCContext &Ctx); | ||
|
|
||
| static const AMDGPUVariadicMCExpr *createOr(ArrayRef<const MCExpr *> Args, | ||
| MCContext &Ctx) { | ||
| return create(VariadicKind::AGVK_Or, Args, Ctx); | ||
| } | ||
|
|
||
| static const AMDGPUVariadicMCExpr *createMax(ArrayRef<const MCExpr *> Args, | ||
| MCContext &Ctx) { | ||
| return create(VariadicKind::AGVK_Max, Args, Ctx); | ||
| } | ||
|
|
||
| VariadicKind getKind() const { return Kind; } | ||
| const MCExpr *getSubExpr(size_t Index) const; | ||
|
|
||
| void printImpl(raw_ostream &OS, const MCAsmInfo *MAI) const override; | ||
| bool evaluateAsRelocatableImpl(MCValue &Res, const MCAsmLayout *Layout, | ||
| const MCFixup *Fixup) const override; | ||
| void visitUsedExpr(MCStreamer &Streamer) const override; | ||
| MCFragment *findAssociatedFragment() const override; | ||
| void fixELFSymbolsInTLSFixups(MCAssembler &) const override{}; | ||
|
|
||
| static bool classof(const MCExpr *E) { | ||
| return E->getKind() == MCExpr::Target; | ||
| } | ||
| }; | ||
|
|
||
| } // end namespace llvm | ||
|
|
||
| #endif // LLVM_LIB_TARGET_AMDGPU_MCTARGETDESC_AMDGPUMCEXPR_H |