Compare commits
30 Commits
main
...
users/myst
Author | SHA1 | Date | |
---|---|---|---|
![]() |
bb4a9036cf | ||
![]() |
c1f1797f44 | ||
![]() |
aa874c9928 | ||
![]() |
fd1ee30e8c | ||
![]() |
4cb90bf6f1 | ||
![]() |
ce0760f8f7 | ||
![]() |
cfd33c8c0a | ||
![]() |
f272839e84 | ||
![]() |
46a0008948 | ||
![]() |
4c53b27ac2 | ||
![]() |
9265309f0c | ||
![]() |
b7f8a9c8b4 | ||
![]() |
68761d9bf3 | ||
![]() |
d11cfe5d18 | ||
![]() |
435ba3597b | ||
![]() |
dff521f73d | ||
![]() |
40bafe2ceb | ||
![]() |
341dd6a3ca | ||
![]() |
75c4d315dc | ||
![]() |
904c996355 | ||
![]() |
a6220fef9b | ||
![]() |
20f2ad062c | ||
![]() |
6fc858761e | ||
![]() |
57ba236e87 | ||
![]() |
07ccb63a1b | ||
![]() |
dd58f21c9e | ||
![]() |
bd4dd9ca02 | ||
![]() |
5a056672cb | ||
![]() |
0e830ad120 | ||
![]() |
f3fdeff3e3 |
@ -5233,3 +5233,14 @@ def NonString : InheritableAttr {
|
|||||||
let Subjects = SubjectList<[Var, Field]>;
|
let Subjects = SubjectList<[Var, Field]>;
|
||||||
let Documentation = [NonStringDocs];
|
let Documentation = [NonStringDocs];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def ModularFormat : InheritableAttr {
|
||||||
|
let Spellings = [Clang<"modular_format">];
|
||||||
|
let Args = [
|
||||||
|
IdentifierArgument<"ModularImplFn">,
|
||||||
|
StringArgument<"ImplName">,
|
||||||
|
VariadicStringArgument<"Aspects">
|
||||||
|
];
|
||||||
|
let Subjects = SubjectList<[Function]>;
|
||||||
|
let Documentation = [ModularFormatDocs];
|
||||||
|
}
|
||||||
|
@ -9549,3 +9549,37 @@ silence diagnostics with code like:
|
|||||||
__attribute__((nonstring)) char NotAStr[3] = "foo"; // Not diagnosed
|
__attribute__((nonstring)) char NotAStr[3] = "foo"; // Not diagnosed
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def ModularFormatDocs : Documentation {
|
||||||
|
let Category = DocCatFunction;
|
||||||
|
let Content = [{
|
||||||
|
The ``modular_format`` attribute can be applied to a function that bears the
|
||||||
|
``format`` attribute (or standard library functions) to indicate that the
|
||||||
|
implementation is modular on the format string argument. When the format string
|
||||||
|
for a given call is constant, the compiler may redirect the call to the symbol
|
||||||
|
given as the first argument to the attribute (the modular implementation
|
||||||
|
function).
|
||||||
|
|
||||||
|
The second argument is a implementation name, and the remaining arguments are
|
||||||
|
aspects of the format string for the compiler to report. If the compiler does
|
||||||
|
not understand an aspect, it must summarily report that the format string has
|
||||||
|
that aspect.
|
||||||
|
|
||||||
|
The compiler reports an aspect by issuing a relocation for the symbol
|
||||||
|
``<impl_name>_<aspect>``. This arranges for code and data needed to support the
|
||||||
|
aspect of the implementation to be brought into the link to satisfy weak
|
||||||
|
references in the modular implemenation function.
|
||||||
|
|
||||||
|
For example, say ``printf`` is annotated with
|
||||||
|
``modular_format(__modular_printf, __printf, float)``. Then, a call to
|
||||||
|
``printf(var, 42)`` would be untouched. A call to ``printf("%d", 42)`` would
|
||||||
|
become a call to ``__modular_printf`` with the same arguments, as would
|
||||||
|
``printf("%f", 42.0)``. The latter would be accompanied with a strong
|
||||||
|
relocation against the symbol ``__printf_float``, which would bring floating
|
||||||
|
point support for ``printf`` into the link.
|
||||||
|
|
||||||
|
The following aspects are currently supported:
|
||||||
|
|
||||||
|
- ``float``: The call has a floating point argument
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
@ -2560,6 +2560,20 @@ void CodeGenModule::ConstructAttributeList(StringRef Name,
|
|||||||
|
|
||||||
if (TargetDecl->hasAttr<ArmLocallyStreamingAttr>())
|
if (TargetDecl->hasAttr<ArmLocallyStreamingAttr>())
|
||||||
FuncAttrs.addAttribute("aarch64_pstate_sm_body");
|
FuncAttrs.addAttribute("aarch64_pstate_sm_body");
|
||||||
|
|
||||||
|
if (auto *ModularFormat = TargetDecl->getAttr<ModularFormatAttr>()) {
|
||||||
|
// TODO: Error checking
|
||||||
|
FormatAttr *Format = TargetDecl->getAttr<FormatAttr>();
|
||||||
|
StringRef Type = Format->getType()->getName();
|
||||||
|
std::string FormatIdx = std::to_string(Format->getFormatIdx());
|
||||||
|
std::string FirstArg = std::to_string(Format->getFirstArg());
|
||||||
|
SmallVector<StringRef> Args = {
|
||||||
|
Type, FormatIdx, FirstArg,
|
||||||
|
ModularFormat->getModularImplFn()->getName(),
|
||||||
|
ModularFormat->getImplName()};
|
||||||
|
llvm::append_range(Args, ModularFormat->aspects());
|
||||||
|
FuncAttrs.addAttribute("modular-format", llvm::join(Args, ","));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attach "no-builtins" attributes to:
|
// Attach "no-builtins" attributes to:
|
||||||
|
@ -6752,6 +6752,29 @@ static void handleVTablePointerAuthentication(Sema &S, Decl *D,
|
|||||||
CustomDiscriminationValue));
|
CustomDiscriminationValue));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void handleModularFormat(Sema &S, Decl *D, const ParsedAttr &AL) {
|
||||||
|
StringRef ImplName;
|
||||||
|
if (!S.checkStringLiteralArgumentAttr(AL, 1, ImplName))
|
||||||
|
return;
|
||||||
|
SmallVector<StringRef> Aspects;
|
||||||
|
for (unsigned I = 2, E = AL.getNumArgs(); I != E; ++I) {
|
||||||
|
StringRef Aspect;
|
||||||
|
if (!S.checkStringLiteralArgumentAttr(AL, I, Aspect))
|
||||||
|
return;
|
||||||
|
Aspects.push_back(Aspect);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store aspects sorted and without duplicates.
|
||||||
|
llvm::sort(Aspects);
|
||||||
|
Aspects.erase(llvm::unique(Aspects), Aspects.end());
|
||||||
|
|
||||||
|
// TODO: Type checking on identifier
|
||||||
|
// TODO: Merge attributes
|
||||||
|
D->addAttr(::new (S.Context) ModularFormatAttr(
|
||||||
|
S.Context, AL, AL.getArgAsIdent(0)->getIdentifierInfo(), ImplName,
|
||||||
|
Aspects.data(), Aspects.size()));
|
||||||
|
}
|
||||||
|
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
// Top Level Sema Entry Points
|
// Top Level Sema Entry Points
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
@ -7679,6 +7702,10 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL,
|
|||||||
case ParsedAttr::AT_VTablePointerAuthentication:
|
case ParsedAttr::AT_VTablePointerAuthentication:
|
||||||
handleVTablePointerAuthentication(S, D, AL);
|
handleVTablePointerAuthentication(S, D, AL);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case ParsedAttr::AT_ModularFormat:
|
||||||
|
handleModularFormat(S, D, AL);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,6 +51,10 @@
|
|||||||
"LIBC_CONF_PRINTF_RUNTIME_DISPATCH": {
|
"LIBC_CONF_PRINTF_RUNTIME_DISPATCH": {
|
||||||
"value": true,
|
"value": true,
|
||||||
"doc": "Use dynamic dispatch for the output mechanism to reduce code size."
|
"doc": "Use dynamic dispatch for the output mechanism to reduce code size."
|
||||||
|
},
|
||||||
|
"LIBC_CONF_PRINTF_MODULAR": {
|
||||||
|
"value": true,
|
||||||
|
"doc": "Split printf implementation into modules that can be lazily linked in."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"scanf": {
|
"scanf": {
|
||||||
|
@ -45,6 +45,7 @@ to learn about the defaults for your platform and target.
|
|||||||
- ``LIBC_CONF_PRINTF_FLOAT_TO_STR_USE_DYADIC_FLOAT``: Use dyadic float for faster and smaller but less accurate printf doubles.
|
- ``LIBC_CONF_PRINTF_FLOAT_TO_STR_USE_DYADIC_FLOAT``: Use dyadic float for faster and smaller but less accurate printf doubles.
|
||||||
- ``LIBC_CONF_PRINTF_FLOAT_TO_STR_USE_FLOAT320``: Use an alternative printf float implementation based on 320-bit floats
|
- ``LIBC_CONF_PRINTF_FLOAT_TO_STR_USE_FLOAT320``: Use an alternative printf float implementation based on 320-bit floats
|
||||||
- ``LIBC_CONF_PRINTF_FLOAT_TO_STR_USE_MEGA_LONG_DOUBLE_TABLE``: Use large table for better printf long double performance.
|
- ``LIBC_CONF_PRINTF_FLOAT_TO_STR_USE_MEGA_LONG_DOUBLE_TABLE``: Use large table for better printf long double performance.
|
||||||
|
- ``LIBC_CONF_PRINTF_MODULAR``: Split printf implementation into modules that can be lazily linked in.
|
||||||
- ``LIBC_CONF_PRINTF_RUNTIME_DISPATCH``: Use dynamic dispatch for the output mechanism to reduce code size.
|
- ``LIBC_CONF_PRINTF_RUNTIME_DISPATCH``: Use dynamic dispatch for the output mechanism to reduce code size.
|
||||||
* **"pthread" options**
|
* **"pthread" options**
|
||||||
- ``LIBC_CONF_RAW_MUTEX_DEFAULT_SPIN_COUNT``: Default number of spins before blocking if a mutex is in contention (default to 100).
|
- ``LIBC_CONF_RAW_MUTEX_DEFAULT_SPIN_COUNT``: Default number of spins before blocking if a mutex is in contention (default to 100).
|
||||||
|
@ -412,10 +412,15 @@ if(LLVM_LIBC_FULL_BUILD)
|
|||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
set(printf_srcs printf.cpp)
|
||||||
|
if (LIBC_CONF_PRINTF_MODULAR)
|
||||||
|
list(APPEND printf_srcs printf_modular.cpp)
|
||||||
|
endif()
|
||||||
|
|
||||||
add_generic_entrypoint_object(
|
add_generic_entrypoint_object(
|
||||||
printf
|
printf
|
||||||
SRCS
|
SRCS
|
||||||
printf.cpp
|
${printf_srcs}
|
||||||
HDRS
|
HDRS
|
||||||
../printf.h
|
../printf.h
|
||||||
DEPENDS
|
DEPENDS
|
||||||
|
40
libc/src/stdio/generic/printf_modular.cpp
Normal file
40
libc/src/stdio/generic/printf_modular.cpp
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
//===-- Implementation of printf_modular-----------------------------------===//
|
||||||
|
//
|
||||||
|
// 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 "src/stdio/printf.h"
|
||||||
|
|
||||||
|
#include "src/__support/File/file.h"
|
||||||
|
#include "src/__support/arg_list.h"
|
||||||
|
#include "src/__support/macros/config.h"
|
||||||
|
#include "src/stdio/printf_core/vfprintf_internal.h"
|
||||||
|
|
||||||
|
#include "hdr/types/FILE.h"
|
||||||
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
#ifndef LIBC_COPT_STDIO_USE_SYSTEM_FILE
|
||||||
|
#define PRINTF_STDOUT LIBC_NAMESPACE::stdout
|
||||||
|
#else // LIBC_COPT_STDIO_USE_SYSTEM_FILE
|
||||||
|
#define PRINTF_STDOUT ::stdout
|
||||||
|
#endif // LIBC_COPT_STDIO_USE_SYSTEM_FILE
|
||||||
|
|
||||||
|
namespace LIBC_NAMESPACE_DECL {
|
||||||
|
|
||||||
|
LLVM_LIBC_FUNCTION(int, __printf_modular,
|
||||||
|
(const char *__restrict format, ...)) {
|
||||||
|
va_list vlist;
|
||||||
|
va_start(vlist, format);
|
||||||
|
internal::ArgList args(vlist); // This holder class allows for easier copying
|
||||||
|
// and pointer semantics, as well as handling
|
||||||
|
// destruction automatically.
|
||||||
|
va_end(vlist);
|
||||||
|
int ret_val = printf_core::vfprintf_internal_modular(
|
||||||
|
reinterpret_cast<::FILE *>(PRINTF_STDOUT), format, args);
|
||||||
|
return ret_val;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace LIBC_NAMESPACE_DECL
|
@ -15,6 +15,7 @@
|
|||||||
namespace LIBC_NAMESPACE_DECL {
|
namespace LIBC_NAMESPACE_DECL {
|
||||||
|
|
||||||
int printf(const char *__restrict format, ...);
|
int printf(const char *__restrict format, ...);
|
||||||
|
int __printf_modular(const char *__restrict format, ...);
|
||||||
|
|
||||||
} // namespace LIBC_NAMESPACE_DECL
|
} // namespace LIBC_NAMESPACE_DECL
|
||||||
|
|
||||||
|
@ -28,6 +28,9 @@ endif()
|
|||||||
if(LIBC_CONF_PRINTF_RUNTIME_DISPATCH)
|
if(LIBC_CONF_PRINTF_RUNTIME_DISPATCH)
|
||||||
list(APPEND printf_config_copts "-DLIBC_COPT_PRINTF_RUNTIME_DISPATCH")
|
list(APPEND printf_config_copts "-DLIBC_COPT_PRINTF_RUNTIME_DISPATCH")
|
||||||
endif()
|
endif()
|
||||||
|
if(LIBC_CONF_PRINTF_MODULAR)
|
||||||
|
list(APPEND printf_config_copts "-DLIBC_COPT_PRINTF_MODULAR")
|
||||||
|
endif()
|
||||||
if(printf_config_copts)
|
if(printf_config_copts)
|
||||||
list(PREPEND printf_config_copts "COMPILE_OPTIONS")
|
list(PREPEND printf_config_copts "COMPILE_OPTIONS")
|
||||||
endif()
|
endif()
|
||||||
@ -114,10 +117,12 @@ add_header_library(
|
|||||||
libc.src.__support.StringUtil.error_to_string
|
libc.src.__support.StringUtil.error_to_string
|
||||||
)
|
)
|
||||||
|
|
||||||
add_header_library(
|
add_object_library(
|
||||||
printf_main
|
printf_main
|
||||||
HDRS
|
HDRS
|
||||||
printf_main.h
|
printf_main.h
|
||||||
|
SRCS
|
||||||
|
float_impl.cpp
|
||||||
DEPENDS
|
DEPENDS
|
||||||
.parser
|
.parser
|
||||||
.converter
|
.converter
|
||||||
|
@ -1122,6 +1122,18 @@ LIBC_INLINE int convert_float_dec_auto_typed(Writer<write_mode> *writer,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <WriteMode write_mode>
|
||||||
|
LIBC_PRINTF_MODULAR_DECL int
|
||||||
|
convert_float_decimal(Writer<write_mode> *writer, const FormatSection &to_conv);
|
||||||
|
template <WriteMode write_mode>
|
||||||
|
LIBC_PRINTF_MODULAR_DECL int
|
||||||
|
convert_float_dec_exp(Writer<write_mode> *writer, const FormatSection &to_conv);
|
||||||
|
template <WriteMode write_mode>
|
||||||
|
LIBC_PRINTF_MODULAR_DECL int
|
||||||
|
convert_float_dec_auto(Writer<write_mode> *writer,
|
||||||
|
const FormatSection &to_conv);
|
||||||
|
|
||||||
|
#ifdef LIBC_PRINTF_DEFINE_MODULAR
|
||||||
// TODO: unify the float converters to remove the duplicated checks for inf/nan.
|
// TODO: unify the float converters to remove the duplicated checks for inf/nan.
|
||||||
|
|
||||||
template <WriteMode write_mode>
|
template <WriteMode write_mode>
|
||||||
@ -1189,6 +1201,7 @@ LIBC_INLINE int convert_float_dec_auto(Writer<write_mode> *writer,
|
|||||||
|
|
||||||
return convert_inf_nan(writer, to_conv);
|
return convert_inf_nan(writer, to_conv);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
} // namespace printf_core
|
} // namespace printf_core
|
||||||
} // namespace LIBC_NAMESPACE_DECL
|
} // namespace LIBC_NAMESPACE_DECL
|
||||||
|
@ -675,6 +675,17 @@ LIBC_INLINE int convert_float_dec_auto_typed(Writer<write_mode> *writer,
|
|||||||
return convert_float_typed<T>(writer, to_conv, float_bits, ConversionType::G);
|
return convert_float_typed<T>(writer, to_conv, float_bits, ConversionType::G);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <WriteMode write_mode>
|
||||||
|
LIBC_PRINTF_MODULAR_DECL int convert_float_decimal(Writer<write_mode> *writer,
|
||||||
|
const FormatSection &to_conv);
|
||||||
|
template <WriteMode write_mode>
|
||||||
|
LIBC_PRINTF_MODULAR_DECL int convert_float_dec_exp(Writer<write_mode> *writer,
|
||||||
|
const FormatSection &to_conv);
|
||||||
|
template <WriteMode write_mode>
|
||||||
|
LIBC_PRINTF_MODULAR_DECL int convert_float_dec_auto(Writer<write_mode> *writer,
|
||||||
|
const FormatSection &to_conv);
|
||||||
|
|
||||||
|
#ifdef LIBC_PRINTF_DEFINE_MODULAR
|
||||||
template <WriteMode write_mode>
|
template <WriteMode write_mode>
|
||||||
LIBC_INLINE int convert_float_decimal(Writer<write_mode> *writer,
|
LIBC_INLINE int convert_float_decimal(Writer<write_mode> *writer,
|
||||||
const FormatSection &to_conv) {
|
const FormatSection &to_conv) {
|
||||||
@ -692,6 +703,7 @@ LIBC_INLINE int convert_float_dec_auto(Writer<write_mode> *writer,
|
|||||||
const FormatSection &to_conv) {
|
const FormatSection &to_conv) {
|
||||||
return convert_float_outer(writer, to_conv, ConversionType::G);
|
return convert_float_outer(writer, to_conv, ConversionType::G);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
} // namespace printf_core
|
} // namespace printf_core
|
||||||
} // namespace LIBC_NAMESPACE_DECL
|
} // namespace LIBC_NAMESPACE_DECL
|
||||||
|
@ -25,6 +25,11 @@
|
|||||||
namespace LIBC_NAMESPACE_DECL {
|
namespace LIBC_NAMESPACE_DECL {
|
||||||
namespace printf_core {
|
namespace printf_core {
|
||||||
|
|
||||||
|
template <WriteMode write_mode>
|
||||||
|
LIBC_PRINTF_MODULAR_DECL int convert_float_hex_exp(Writer<write_mode> *writer,
|
||||||
|
const FormatSection &to_conv);
|
||||||
|
|
||||||
|
#ifdef LIBC_PRINTF_DEFINE_MODULAR
|
||||||
template <WriteMode write_mode>
|
template <WriteMode write_mode>
|
||||||
LIBC_INLINE int convert_float_hex_exp(Writer<write_mode> *writer,
|
LIBC_INLINE int convert_float_hex_exp(Writer<write_mode> *writer,
|
||||||
const FormatSection &to_conv) {
|
const FormatSection &to_conv) {
|
||||||
@ -254,6 +259,7 @@ LIBC_INLINE int convert_float_hex_exp(Writer<write_mode> *writer,
|
|||||||
}
|
}
|
||||||
return WRITE_OK;
|
return WRITE_OK;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
} // namespace printf_core
|
} // namespace printf_core
|
||||||
} // namespace LIBC_NAMESPACE_DECL
|
} // namespace LIBC_NAMESPACE_DECL
|
||||||
|
56
libc/src/stdio/printf_core/float_impl.cpp
Normal file
56
libc/src/stdio/printf_core/float_impl.cpp
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
//
|
||||||
|
// 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
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
///
|
||||||
|
/// \file
|
||||||
|
/// This file instantiates the functionality needed for supporting floating
|
||||||
|
/// point arguments in modular printf builds. Non-modular printf builds
|
||||||
|
/// implicitly instantiate these functions.
|
||||||
|
///
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#ifdef LIBC_COPT_PRINTF_MODULAR
|
||||||
|
#include "src/__support/arg_list.h"
|
||||||
|
|
||||||
|
#define LIBC_PRINTF_DEFINE_MODULAR
|
||||||
|
#include "src/stdio/printf_core/float_dec_converter.h"
|
||||||
|
#include "src/stdio/printf_core/float_hex_converter.h"
|
||||||
|
#include "src/stdio/printf_core/parser.h"
|
||||||
|
|
||||||
|
namespace LIBC_NAMESPACE_DECL {
|
||||||
|
namespace printf_core {
|
||||||
|
template class Parser<internal::ArgList>;
|
||||||
|
template class Parser<internal::DummyArgList<false>>;
|
||||||
|
template class Parser<internal::DummyArgList<true>>;
|
||||||
|
template class Parser<internal::StructArgList<false>>;
|
||||||
|
template class Parser<internal::StructArgList<true>>;
|
||||||
|
|
||||||
|
#define INSTANTIATE_CONVERT_FN(NAME) \
|
||||||
|
template int NAME<WriteMode::FILL_BUFF_AND_DROP_OVERFLOW>( \
|
||||||
|
Writer<WriteMode::FILL_BUFF_AND_DROP_OVERFLOW> * writer, \
|
||||||
|
const FormatSection &to_conv); \
|
||||||
|
template int NAME<WriteMode::FLUSH_TO_STREAM>( \
|
||||||
|
Writer<WriteMode::FLUSH_TO_STREAM> * writer, \
|
||||||
|
const FormatSection &to_conv); \
|
||||||
|
template int NAME<WriteMode::RESIZE_AND_FILL_BUFF>( \
|
||||||
|
Writer<WriteMode::RESIZE_AND_FILL_BUFF> * writer, \
|
||||||
|
const FormatSection &to_conv); \
|
||||||
|
template int NAME<WriteMode::RUNTIME_DISPATCH>( \
|
||||||
|
Writer<WriteMode::RUNTIME_DISPATCH> * writer, \
|
||||||
|
const FormatSection &to_conv)
|
||||||
|
|
||||||
|
INSTANTIATE_CONVERT_FN(convert_float_decimal);
|
||||||
|
INSTANTIATE_CONVERT_FN(convert_float_dec_exp);
|
||||||
|
INSTANTIATE_CONVERT_FN(convert_float_dec_auto);
|
||||||
|
INSTANTIATE_CONVERT_FN(convert_float_hex_exp);
|
||||||
|
|
||||||
|
} // namespace printf_core
|
||||||
|
} // namespace LIBC_NAMESPACE_DECL
|
||||||
|
|
||||||
|
// Bring this file into the link if __printf_float is referenced.
|
||||||
|
extern "C" void __printf_float() {}
|
||||||
|
#endif
|
@ -236,11 +236,7 @@ public:
|
|||||||
case ('A'):
|
case ('A'):
|
||||||
case ('g'):
|
case ('g'):
|
||||||
case ('G'):
|
case ('G'):
|
||||||
if (lm != LengthModifier::L) {
|
write_float_arg_val(section, lm, conv_index);
|
||||||
WRITE_ARG_VAL_SIMPLEST(section.conv_val_raw, double, conv_index);
|
|
||||||
} else {
|
|
||||||
WRITE_ARG_VAL_SIMPLEST(section.conv_val_raw, long double, conv_index);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
#endif // LIBC_COPT_PRINTF_DISABLE_FLOAT
|
#endif // LIBC_COPT_PRINTF_DISABLE_FLOAT
|
||||||
#ifdef LIBC_INTERNAL_PRINTF_HAS_FIXED_POINT
|
#ifdef LIBC_INTERNAL_PRINTF_HAS_FIXED_POINT
|
||||||
@ -299,6 +295,12 @@ public:
|
|||||||
return section;
|
return section;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LIBC_PRINTF_MODULAR_DECL void write_float_arg_val(FormatSection §ion,
|
||||||
|
LengthModifier lm,
|
||||||
|
size_t conv_index);
|
||||||
|
LIBC_PRINTF_MODULAR_DECL TypeDesc float_type_desc(LengthModifier lm);
|
||||||
|
LIBC_PRINTF_MODULAR_DECL bool advance_arg_if_float(TypeDesc cur_type_desc);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// parse_flags parses the flags inside a format string. It assumes that
|
// parse_flags parses the flags inside a format string. It assumes that
|
||||||
// str[*local_pos] is inside a format specifier, and parses any flags it
|
// str[*local_pos] is inside a format specifier, and parses any flags it
|
||||||
@ -474,10 +476,9 @@ private:
|
|||||||
args_cur.template next_var<uint64_t>();
|
args_cur.template next_var<uint64_t>();
|
||||||
#ifndef LIBC_COPT_PRINTF_DISABLE_FLOAT
|
#ifndef LIBC_COPT_PRINTF_DISABLE_FLOAT
|
||||||
// Floating point numbers are stored separately from the other arguments.
|
// Floating point numbers are stored separately from the other arguments.
|
||||||
else if (cur_type_desc == type_desc_from_type<double>())
|
else if (&Parser::advance_arg_if_float &&
|
||||||
args_cur.template next_var<double>();
|
advance_arg_if_float(cur_type_desc))
|
||||||
else if (cur_type_desc == type_desc_from_type<long double>())
|
;
|
||||||
args_cur.template next_var<long double>();
|
|
||||||
#endif // LIBC_COPT_PRINTF_DISABLE_FLOAT
|
#endif // LIBC_COPT_PRINTF_DISABLE_FLOAT
|
||||||
#ifdef LIBC_INTERNAL_PRINTF_HAS_FIXED_POINT
|
#ifdef LIBC_INTERNAL_PRINTF_HAS_FIXED_POINT
|
||||||
// Floating point numbers may be stored separately from the other
|
// Floating point numbers may be stored separately from the other
|
||||||
@ -630,10 +631,7 @@ private:
|
|||||||
case ('A'):
|
case ('A'):
|
||||||
case ('g'):
|
case ('g'):
|
||||||
case ('G'):
|
case ('G'):
|
||||||
if (lm != LengthModifier::L)
|
conv_size = float_type_desc(lm);
|
||||||
conv_size = type_desc_from_type<double>();
|
|
||||||
else
|
|
||||||
conv_size = type_desc_from_type<long double>();
|
|
||||||
break;
|
break;
|
||||||
#endif // LIBC_COPT_PRINTF_DISABLE_FLOAT
|
#endif // LIBC_COPT_PRINTF_DISABLE_FLOAT
|
||||||
#ifdef LIBC_INTERNAL_PRINTF_HAS_FIXED_POINT
|
#ifdef LIBC_INTERNAL_PRINTF_HAS_FIXED_POINT
|
||||||
@ -682,6 +680,39 @@ private:
|
|||||||
#endif // LIBC_COPT_PRINTF_DISABLE_INDEX_MODE
|
#endif // LIBC_COPT_PRINTF_DISABLE_INDEX_MODE
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#ifdef LIBC_PRINTF_DEFINE_MODULAR
|
||||||
|
template <typename ArgParser>
|
||||||
|
LIBC_INLINE void Parser<ArgParser>::write_float_arg_val(FormatSection §ion,
|
||||||
|
LengthModifier lm,
|
||||||
|
size_t conv_index) {
|
||||||
|
if (lm != LengthModifier::L) {
|
||||||
|
WRITE_ARG_VAL_SIMPLEST(section.conv_val_raw, double, conv_index);
|
||||||
|
} else {
|
||||||
|
WRITE_ARG_VAL_SIMPLEST(section.conv_val_raw, long double, conv_index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename ArgParser>
|
||||||
|
LIBC_INLINE TypeDesc Parser<ArgParser>::float_type_desc(LengthModifier lm) {
|
||||||
|
if (lm != LengthModifier::L)
|
||||||
|
return type_desc_from_type<double>();
|
||||||
|
else
|
||||||
|
return type_desc_from_type<long double>();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename ArgParser>
|
||||||
|
LIBC_INLINE bool
|
||||||
|
Parser<ArgParser>::advance_arg_if_float(TypeDesc cur_type_desc) {
|
||||||
|
if (cur_type_desc == type_desc_from_type<double>())
|
||||||
|
args_cur.template next_var<double>();
|
||||||
|
else if (cur_type_desc == type_desc_from_type<long double>())
|
||||||
|
args_cur.template next_var<long double>();
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
} // namespace printf_core
|
} // namespace printf_core
|
||||||
} // namespace LIBC_NAMESPACE_DECL
|
} // namespace LIBC_NAMESPACE_DECL
|
||||||
|
|
||||||
|
@ -48,4 +48,11 @@
|
|||||||
|
|
||||||
// LIBC_COPT_PRINTF_NO_NULLPTR_CHECKS
|
// LIBC_COPT_PRINTF_NO_NULLPTR_CHECKS
|
||||||
|
|
||||||
|
#ifdef LIBC_COPT_PRINTF_MODULAR
|
||||||
|
#define LIBC_PRINTF_MODULAR_DECL [[gnu::weak]]
|
||||||
|
#else
|
||||||
|
#define LIBC_PRINTF_MODULAR_DECL LIBC_INLINE
|
||||||
|
#define LIBC_PRINTF_DEFINE_MODULAR
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif // LLVM_LIBC_SRC_STDIO_PRINTF_CORE_PRINTF_CONFIG_H
|
#endif // LLVM_LIBC_SRC_STDIO_PRINTF_CORE_PRINTF_CONFIG_H
|
||||||
|
@ -22,8 +22,8 @@ namespace LIBC_NAMESPACE_DECL {
|
|||||||
namespace printf_core {
|
namespace printf_core {
|
||||||
|
|
||||||
template <WriteMode write_mode>
|
template <WriteMode write_mode>
|
||||||
int printf_main(Writer<write_mode> *writer, const char *__restrict str,
|
int printf_main_modular(Writer<write_mode> *writer, const char *__restrict str,
|
||||||
internal::ArgList &args) {
|
internal::ArgList &args) {
|
||||||
Parser<internal::ArgList> parser(str, args);
|
Parser<internal::ArgList> parser(str, args);
|
||||||
int result = 0;
|
int result = 0;
|
||||||
for (FormatSection cur_section = parser.get_next_section();
|
for (FormatSection cur_section = parser.get_next_section();
|
||||||
@ -41,6 +41,15 @@ int printf_main(Writer<write_mode> *writer, const char *__restrict str,
|
|||||||
return writer->get_chars_written();
|
return writer->get_chars_written();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <WriteMode write_mode>
|
||||||
|
int printf_main(Writer<write_mode> *writer, const char *__restrict str,
|
||||||
|
internal::ArgList &args) {
|
||||||
|
#ifdef LIBC_COPT_PRINTF_MODULAR
|
||||||
|
LIBC_INLINE_ASM(".reloc ., BFD_RELOC_NONE, __printf_float");
|
||||||
|
#endif
|
||||||
|
return printf_main_modular(writer, str, args);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace printf_core
|
} // namespace printf_core
|
||||||
} // namespace LIBC_NAMESPACE_DECL
|
} // namespace LIBC_NAMESPACE_DECL
|
||||||
|
|
||||||
|
@ -67,7 +67,7 @@ LIBC_INLINE int file_write_hook(cpp::string_view new_str, void *fp) {
|
|||||||
return WRITE_OK;
|
return WRITE_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
LIBC_INLINE int vfprintf_internal(::FILE *__restrict stream,
|
LIBC_INLINE int vfprintf_internal_modular(::FILE *__restrict stream,
|
||||||
const char *__restrict format,
|
const char *__restrict format,
|
||||||
internal::ArgList &args) {
|
internal::ArgList &args) {
|
||||||
constexpr size_t BUFF_SIZE = 1024;
|
constexpr size_t BUFF_SIZE = 1024;
|
||||||
@ -76,7 +76,7 @@ LIBC_INLINE int vfprintf_internal(::FILE *__restrict stream,
|
|||||||
buffer, BUFF_SIZE, &file_write_hook, reinterpret_cast<void *>(stream));
|
buffer, BUFF_SIZE, &file_write_hook, reinterpret_cast<void *>(stream));
|
||||||
Writer writer(wb);
|
Writer writer(wb);
|
||||||
internal::flockfile(stream);
|
internal::flockfile(stream);
|
||||||
int retval = printf_main(&writer, format, args);
|
int retval = printf_main_modular(&writer, format, args);
|
||||||
int flushval = wb.overflow_write("");
|
int flushval = wb.overflow_write("");
|
||||||
if (flushval != WRITE_OK)
|
if (flushval != WRITE_OK)
|
||||||
retval = flushval;
|
retval = flushval;
|
||||||
@ -84,6 +84,15 @@ LIBC_INLINE int vfprintf_internal(::FILE *__restrict stream,
|
|||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LIBC_INLINE int vfprintf_internal(::FILE *__restrict stream,
|
||||||
|
const char *__restrict format,
|
||||||
|
internal::ArgList &args) {
|
||||||
|
#ifdef LIBC_COPT_PRINTF_MODULAR
|
||||||
|
__asm__ __volatile__(".reloc ., BFD_RELOC_NONE, __printf_float");
|
||||||
|
#endif
|
||||||
|
return vfprintf_internal_modular(stream, format, args);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace printf_core
|
} // namespace printf_core
|
||||||
} // namespace LIBC_NAMESPACE_DECL
|
} // namespace LIBC_NAMESPACE_DECL
|
||||||
|
|
||||||
|
@ -2635,6 +2635,28 @@ For example:
|
|||||||
This attribute indicates that outlining passes should not modify the
|
This attribute indicates that outlining passes should not modify the
|
||||||
function.
|
function.
|
||||||
|
|
||||||
|
``"modular-format"="<type>,<string_idx>,<first_idx_to_check>,<modular_impl_fn>,<impl_name>,<aspects...>"``
|
||||||
|
This attribute indicates that the implementation is modular on a particular
|
||||||
|
format string argument . When the argument for a given call is constant, the
|
||||||
|
compiler may redirect the call to a modular implementation function
|
||||||
|
instead.
|
||||||
|
|
||||||
|
The compiler also emits relocations to report various aspects of the format
|
||||||
|
string and arguments that were present. The compiler reports an aspect by
|
||||||
|
issuing a relocation for the symbol `<impl_name>_<aspect>``. This arranges
|
||||||
|
for code and data needed to support the aspect of the implementation to be
|
||||||
|
brought into the link to satisfy weak references in the modular
|
||||||
|
implemenation function.
|
||||||
|
|
||||||
|
The first three arguments have the same semantics as the arguments to the C
|
||||||
|
``format`` attribute.
|
||||||
|
|
||||||
|
The following aspects are currently supported:
|
||||||
|
|
||||||
|
- ``float``: The call has a floating point argument
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Call Site Attributes
|
Call Site Attributes
|
||||||
----------------------
|
----------------------
|
||||||
|
|
||||||
@ -30630,6 +30652,38 @@ This intrinsic does nothing, but optimizers must consider it a use of its single
|
|||||||
operand and should try to preserve the intrinsic and its position in the
|
operand and should try to preserve the intrinsic and its position in the
|
||||||
function.
|
function.
|
||||||
|
|
||||||
|
.. _llvm_reloc_none:
|
||||||
|
|
||||||
|
'``llvm.reloc.none``' Intrinsic
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
Syntax:
|
||||||
|
"""""""
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
declare void @llvm.reloc.none(ptrty %ptr)
|
||||||
|
|
||||||
|
Overview:
|
||||||
|
"""""""""
|
||||||
|
|
||||||
|
The ``llvm.reloc.none`` intrinsic emits a no-op relocation against a given
|
||||||
|
operand symbol. This can bring the symbol
|
||||||
|
definition into the link without emitting any code or data to the binary for
|
||||||
|
that purpose.
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
""""""""""
|
||||||
|
|
||||||
|
The ``llvm.reloc.none`` intrinsic takes one argument, which may be any global
|
||||||
|
value.
|
||||||
|
|
||||||
|
Semantics:
|
||||||
|
""""""""""
|
||||||
|
|
||||||
|
This intrinsic emits a no-op relocation at the location of the intrinsic call
|
||||||
|
for the symbol that corresponds to the global value argument.
|
||||||
|
|
||||||
|
|
||||||
Stack Map Intrinsics
|
Stack Map Intrinsics
|
||||||
--------------------
|
--------------------
|
||||||
|
@ -1531,6 +1531,9 @@ enum NodeType {
|
|||||||
#define BEGIN_REGISTER_VP_SDNODE(VPSDID, ...) VPSDID,
|
#define BEGIN_REGISTER_VP_SDNODE(VPSDID, ...) VPSDID,
|
||||||
#include "llvm/IR/VPIntrinsics.def"
|
#include "llvm/IR/VPIntrinsics.def"
|
||||||
|
|
||||||
|
// Issue a no-op relocation against a given symbol at the current location.
|
||||||
|
RELOC_NONE,
|
||||||
|
|
||||||
// The `llvm.experimental.convergence.*` intrinsics.
|
// The `llvm.experimental.convergence.*` intrinsics.
|
||||||
CONVERGENCECTRL_ANCHOR,
|
CONVERGENCECTRL_ANCHOR,
|
||||||
CONVERGENCECTRL_ENTRY,
|
CONVERGENCECTRL_ENTRY,
|
||||||
|
@ -473,6 +473,7 @@ private:
|
|||||||
void Select_WRITE_REGISTER(SDNode *Op);
|
void Select_WRITE_REGISTER(SDNode *Op);
|
||||||
void Select_UNDEF(SDNode *N);
|
void Select_UNDEF(SDNode *N);
|
||||||
void Select_FAKE_USE(SDNode *N);
|
void Select_FAKE_USE(SDNode *N);
|
||||||
|
void Select_RELOC_NONE(SDNode *N);
|
||||||
void CannotYetSelect(SDNode *N);
|
void CannotYetSelect(SDNode *N);
|
||||||
|
|
||||||
void Select_FREEZE(SDNode *N);
|
void Select_FREEZE(SDNode *N);
|
||||||
|
@ -1913,6 +1913,9 @@ def int_threadlocal_address : DefaultAttrsIntrinsic<[llvm_anyptr_ty], [LLVMMatch
|
|||||||
def int_stepvector : DefaultAttrsIntrinsic<[llvm_anyvector_ty],
|
def int_stepvector : DefaultAttrsIntrinsic<[llvm_anyvector_ty],
|
||||||
[], [IntrNoMem]>;
|
[], [IntrNoMem]>;
|
||||||
|
|
||||||
|
def int_reloc_none : DefaultAttrsIntrinsic<[], [llvm_ptr_ty],
|
||||||
|
[IntrHasSideEffects, IntrInaccessibleMemOnly, IntrWillReturn]>;
|
||||||
|
|
||||||
//===---------------- Vector Predication Intrinsics --------------===//
|
//===---------------- Vector Predication Intrinsics --------------===//
|
||||||
// Memory Intrinsics
|
// Memory Intrinsics
|
||||||
def int_vp_store : DefaultAttrsIntrinsic<[],
|
def int_vp_store : DefaultAttrsIntrinsic<[],
|
||||||
|
@ -233,6 +233,9 @@ HANDLE_TARGET_OPCODE(MEMBARRIER)
|
|||||||
// using.
|
// using.
|
||||||
HANDLE_TARGET_OPCODE(JUMP_TABLE_DEBUG_INFO)
|
HANDLE_TARGET_OPCODE(JUMP_TABLE_DEBUG_INFO)
|
||||||
|
|
||||||
|
// Issue a no-op relocation against a given symbol at the current location.
|
||||||
|
HANDLE_TARGET_OPCODE(RELOC_NONE)
|
||||||
|
|
||||||
HANDLE_TARGET_OPCODE(CONVERGENCECTRL_ENTRY)
|
HANDLE_TARGET_OPCODE(CONVERGENCECTRL_ENTRY)
|
||||||
HANDLE_TARGET_OPCODE(CONVERGENCECTRL_ANCHOR)
|
HANDLE_TARGET_OPCODE(CONVERGENCECTRL_ANCHOR)
|
||||||
HANDLE_TARGET_OPCODE(CONVERGENCECTRL_LOOP)
|
HANDLE_TARGET_OPCODE(CONVERGENCECTRL_LOOP)
|
||||||
|
@ -1532,6 +1532,11 @@ def JUMP_TABLE_DEBUG_INFO : StandardPseudoInstruction {
|
|||||||
let Size = 0;
|
let Size = 0;
|
||||||
let isMeta = true;
|
let isMeta = true;
|
||||||
}
|
}
|
||||||
|
def RELOC_NONE : StandardPseudoInstruction {
|
||||||
|
let OutOperandList = (outs);
|
||||||
|
let InOperandList = (ins unknown:$symbol);
|
||||||
|
let hasSideEffects = true;
|
||||||
|
}
|
||||||
|
|
||||||
let hasSideEffects = false, isMeta = true, isConvergent = true in {
|
let hasSideEffects = false, isMeta = true, isConvergent = true in {
|
||||||
def CONVERGENCECTRL_ANCHOR : StandardPseudoInstruction {
|
def CONVERGENCECTRL_ANCHOR : StandardPseudoInstruction {
|
||||||
|
@ -2039,6 +2039,16 @@ void AsmPrinter::emitFunctionBody() {
|
|||||||
// This is only used to influence register allocation behavior, no
|
// This is only used to influence register allocation behavior, no
|
||||||
// actual initialization is needed.
|
// actual initialization is needed.
|
||||||
break;
|
break;
|
||||||
|
case TargetOpcode::RELOC_NONE: {
|
||||||
|
// Generate a temporary label for the current PC.
|
||||||
|
MCSymbol *Sym = OutContext.createTempSymbol("reloc_none");
|
||||||
|
OutStreamer->emitLabel(Sym);
|
||||||
|
const MCExpr *Dot = MCSymbolRefExpr::create(Sym, OutContext);
|
||||||
|
const MCExpr *Value = MCSymbolRefExpr::create(
|
||||||
|
getSymbol(MI.getOperand(0).getGlobal()), OutContext);
|
||||||
|
OutStreamer->emitRelocDirective(*Dot, "BFD_RELOC_NONE", Value, SMLoc());
|
||||||
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
emitInstruction(&MI);
|
emitInstruction(&MI);
|
||||||
|
|
||||||
|
@ -2669,6 +2669,11 @@ bool IRTranslator::translateKnownIntrinsic(const CallInst &CI, Intrinsic::ID ID,
|
|||||||
case Intrinsic::experimental_convergence_entry:
|
case Intrinsic::experimental_convergence_entry:
|
||||||
case Intrinsic::experimental_convergence_loop:
|
case Intrinsic::experimental_convergence_loop:
|
||||||
return translateConvergenceControlIntrinsic(CI, ID, MIRBuilder);
|
return translateConvergenceControlIntrinsic(CI, ID, MIRBuilder);
|
||||||
|
case Intrinsic::reloc_none: {
|
||||||
|
MIRBuilder.buildInstr(TargetOpcode::RELOC_NONE)
|
||||||
|
.addGlobalAddress(cast<GlobalValue>(CI.getArgOperand(0)));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -7751,6 +7751,17 @@ void SelectionDAGBuilder::visitIntrinsicCall(const CallInst &I,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case Intrinsic::reloc_none: {
|
||||||
|
SDValue V = getValue(I.getArgOperand(0));
|
||||||
|
const auto *GA = cast<GlobalAddressSDNode>(V);
|
||||||
|
SDValue Ops[2];
|
||||||
|
Ops[0] = getRoot();
|
||||||
|
Ops[1] = DAG.getTargetGlobalAddress(GA->getGlobal(), sdl, V.getValueType(),
|
||||||
|
GA->getOffset());
|
||||||
|
DAG.setRoot(DAG.getNode(ISD::RELOC_NONE, sdl, MVT::Other, Ops));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
case Intrinsic::eh_exceptionpointer:
|
case Intrinsic::eh_exceptionpointer:
|
||||||
case Intrinsic::eh_exceptioncode: {
|
case Intrinsic::eh_exceptioncode: {
|
||||||
// Get the exception pointer vreg, copy from it, and resize it to fit.
|
// Get the exception pointer vreg, copy from it, and resize it to fit.
|
||||||
|
@ -471,6 +471,8 @@ std::string SDNode::getOperationName(const SelectionDAG *G) const {
|
|||||||
case ISD::LIFETIME_END: return "lifetime.end";
|
case ISD::LIFETIME_END: return "lifetime.end";
|
||||||
case ISD::FAKE_USE:
|
case ISD::FAKE_USE:
|
||||||
return "fake_use";
|
return "fake_use";
|
||||||
|
case ISD::RELOC_NONE:
|
||||||
|
return "reloc_none";
|
||||||
case ISD::PSEUDO_PROBE:
|
case ISD::PSEUDO_PROBE:
|
||||||
return "pseudoprobe";
|
return "pseudoprobe";
|
||||||
case ISD::GC_TRANSITION_START: return "gc_transition.start";
|
case ISD::GC_TRANSITION_START: return "gc_transition.start";
|
||||||
|
@ -2521,6 +2521,11 @@ void SelectionDAGISel::Select_FAKE_USE(SDNode *N) {
|
|||||||
N->getOperand(1), N->getOperand(0));
|
N->getOperand(1), N->getOperand(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SelectionDAGISel::Select_RELOC_NONE(SDNode *N) {
|
||||||
|
CurDAG->SelectNodeTo(N, TargetOpcode::RELOC_NONE, N->getValueType(0),
|
||||||
|
N->getOperand(1), N->getOperand(0));
|
||||||
|
}
|
||||||
|
|
||||||
void SelectionDAGISel::Select_FREEZE(SDNode *N) {
|
void SelectionDAGISel::Select_FREEZE(SDNode *N) {
|
||||||
// TODO: We don't have FREEZE pseudo-instruction in MachineInstr-level now.
|
// TODO: We don't have FREEZE pseudo-instruction in MachineInstr-level now.
|
||||||
// If FREEZE instruction is added later, the code below must be changed as
|
// If FREEZE instruction is added later, the code below must be changed as
|
||||||
@ -3296,6 +3301,9 @@ void SelectionDAGISel::SelectCodeCommon(SDNode *NodeToMatch,
|
|||||||
case ISD::FAKE_USE:
|
case ISD::FAKE_USE:
|
||||||
Select_FAKE_USE(NodeToMatch);
|
Select_FAKE_USE(NodeToMatch);
|
||||||
return;
|
return;
|
||||||
|
case ISD::RELOC_NONE:
|
||||||
|
Select_RELOC_NONE(NodeToMatch);
|
||||||
|
return;
|
||||||
case ISD::FREEZE:
|
case ISD::FREEZE:
|
||||||
Select_FREEZE(NodeToMatch);
|
Select_FREEZE(NodeToMatch);
|
||||||
return;
|
return;
|
||||||
|
@ -5921,6 +5921,11 @@ void Verifier::visitIntrinsicCall(Intrinsic::ID ID, CallBase &Call) {
|
|||||||
Check(cast<ConstantInt>(Call.getArgOperand(3))->getZExtValue() < 2,
|
Check(cast<ConstantInt>(Call.getArgOperand(3))->getZExtValue() < 2,
|
||||||
"cache type argument to llvm.prefetch must be 0-1", Call);
|
"cache type argument to llvm.prefetch must be 0-1", Call);
|
||||||
break;
|
break;
|
||||||
|
case Intrinsic::reloc_none: {
|
||||||
|
Check(isa<GlobalValue>(Call.getArgOperand(0)),
|
||||||
|
"llvm.reloc.none argument must be a global value", &Call);
|
||||||
|
break;
|
||||||
|
}
|
||||||
case Intrinsic::stackprotector:
|
case Intrinsic::stackprotector:
|
||||||
Check(isa<AllocaInst>(Call.getArgOperand(1)->stripPointerCasts()),
|
Check(isa<AllocaInst>(Call.getArgOperand(1)->stripPointerCasts()),
|
||||||
"llvm.stackprotector parameter #2 must resolve to an alloca.", Call);
|
"llvm.stackprotector parameter #2 must resolve to an alloca.", Call);
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
#include "llvm/ADT/SmallBitVector.h"
|
#include "llvm/ADT/SmallBitVector.h"
|
||||||
#include "llvm/ADT/SmallVector.h"
|
#include "llvm/ADT/SmallVector.h"
|
||||||
#include "llvm/ADT/Statistic.h"
|
#include "llvm/ADT/Statistic.h"
|
||||||
|
#include "llvm/ADT/StringExtras.h"
|
||||||
#include "llvm/Analysis/AliasAnalysis.h"
|
#include "llvm/Analysis/AliasAnalysis.h"
|
||||||
#include "llvm/Analysis/AssumeBundleQueries.h"
|
#include "llvm/Analysis/AssumeBundleQueries.h"
|
||||||
#include "llvm/Analysis/AssumptionCache.h"
|
#include "llvm/Analysis/AssumptionCache.h"
|
||||||
@ -4001,6 +4002,63 @@ Instruction *InstCombinerImpl::visitCallBrInst(CallBrInst &CBI) {
|
|||||||
return visitCallBase(CBI);
|
return visitCallBase(CBI);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Value *optimizeModularFormat(CallInst *CI, IRBuilderBase &B) {
|
||||||
|
if (!CI->hasFnAttr("modular-format"))
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
SmallVector<StringRef> Args(
|
||||||
|
llvm::split(CI->getFnAttr("modular-format").getValueAsString(), ','));
|
||||||
|
// TODO: Make use of the first two arguments
|
||||||
|
// TODO: Error handling
|
||||||
|
unsigned FirstArgIdx;
|
||||||
|
if (!llvm::to_integer(Args[2], FirstArgIdx))
|
||||||
|
return nullptr;
|
||||||
|
if (FirstArgIdx == 0)
|
||||||
|
return nullptr;
|
||||||
|
--FirstArgIdx;
|
||||||
|
StringRef FnName = Args[3];
|
||||||
|
StringRef ImplName = Args[4];
|
||||||
|
DenseSet<StringRef> Aspects(llvm::from_range,
|
||||||
|
ArrayRef<StringRef>(Args).drop_front(5));
|
||||||
|
Module *M = CI->getModule();
|
||||||
|
Function *Callee = CI->getCalledFunction();
|
||||||
|
FunctionCallee ModularFn =
|
||||||
|
M->getOrInsertFunction(FnName, Callee->getFunctionType(),
|
||||||
|
Callee->getAttributes().removeFnAttribute(
|
||||||
|
M->getContext(), "modular-format"));
|
||||||
|
CallInst *New = cast<CallInst>(CI->clone());
|
||||||
|
New->setCalledFunction(ModularFn);
|
||||||
|
New->removeFnAttr("modular-format");
|
||||||
|
B.Insert(New);
|
||||||
|
|
||||||
|
const auto ReferenceAspect = [&](StringRef Aspect) {
|
||||||
|
SmallString<20> Name = ImplName;
|
||||||
|
Name += '_';
|
||||||
|
Name += Aspect;
|
||||||
|
Constant *Sym =
|
||||||
|
M->getOrInsertGlobal(Name, Type::getInt8Ty(M->getContext()));
|
||||||
|
Function *RelocNoneFn =
|
||||||
|
Intrinsic::getOrInsertDeclaration(M, Intrinsic::reloc_none);
|
||||||
|
B.CreateCall(RelocNoneFn, {Sym});
|
||||||
|
};
|
||||||
|
|
||||||
|
if (Aspects.contains("float")) {
|
||||||
|
Aspects.erase("float");
|
||||||
|
if (llvm::any_of(
|
||||||
|
llvm::make_range(std::next(CI->arg_begin(), FirstArgIdx),
|
||||||
|
CI->arg_end()),
|
||||||
|
[](Value *V) { return V->getType()->isFloatingPointTy(); }))
|
||||||
|
ReferenceAspect("float");
|
||||||
|
}
|
||||||
|
|
||||||
|
SmallVector<StringRef> UnknownAspects(Aspects.begin(), Aspects.end());
|
||||||
|
llvm::sort(UnknownAspects);
|
||||||
|
for (StringRef Request : UnknownAspects)
|
||||||
|
ReferenceAspect(Request);
|
||||||
|
|
||||||
|
return New;
|
||||||
|
}
|
||||||
|
|
||||||
Instruction *InstCombinerImpl::tryOptimizeCall(CallInst *CI) {
|
Instruction *InstCombinerImpl::tryOptimizeCall(CallInst *CI) {
|
||||||
if (!CI->getCalledFunction()) return nullptr;
|
if (!CI->getCalledFunction()) return nullptr;
|
||||||
|
|
||||||
@ -4022,6 +4080,10 @@ Instruction *InstCombinerImpl::tryOptimizeCall(CallInst *CI) {
|
|||||||
++NumSimplified;
|
++NumSimplified;
|
||||||
return CI->use_empty() ? CI : replaceInstUsesWith(*CI, With);
|
return CI->use_empty() ? CI : replaceInstUsesWith(*CI, With);
|
||||||
}
|
}
|
||||||
|
if (Value *With = optimizeModularFormat(CI, Builder)) {
|
||||||
|
++NumSimplified;
|
||||||
|
return CI->use_empty() ? CI : replaceInstUsesWith(*CI, With);
|
||||||
|
}
|
||||||
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
@ -70,11 +70,11 @@
|
|||||||
# DEBUG-NEXT: .. the first uncovered type index: 1, OK
|
# DEBUG-NEXT: .. the first uncovered type index: 1, OK
|
||||||
# DEBUG-NEXT: .. the first uncovered imm index: 0, OK
|
# DEBUG-NEXT: .. the first uncovered imm index: 0, OK
|
||||||
#
|
#
|
||||||
# DEBUG-NEXT: G_ABDS (opcode 65): 1 type index, 0 imm indices
|
# DEBUG-NEXT: G_ABDS (opcode 66): 1 type index, 0 imm indices
|
||||||
# DEBUG-NEXT: .. type index coverage check SKIPPED: user-defined predicate detected
|
# DEBUG-NEXT: .. type index coverage check SKIPPED: user-defined predicate detected
|
||||||
# DEBUG-NEXT: .. imm index coverage check SKIPPED: user-defined predicate detected
|
# DEBUG-NEXT: .. imm index coverage check SKIPPED: user-defined predicate detected
|
||||||
#
|
#
|
||||||
# DEBUG-NEXT: G_ABDU (opcode 66): 1 type index, 0 imm indices
|
# DEBUG-NEXT: G_ABDU (opcode 67): 1 type index, 0 imm indices
|
||||||
# DEBUG-NEXT: .. opcode {{[0-9]+}} is aliased to {{[0-9]+}}
|
# DEBUG-NEXT: .. opcode {{[0-9]+}} is aliased to {{[0-9]+}}
|
||||||
# DEBUG-NEXT: .. type index coverage check SKIPPED: user-defined predicate detected
|
# DEBUG-NEXT: .. type index coverage check SKIPPED: user-defined predicate detected
|
||||||
# DEBUG-NEXT: .. imm index coverage check SKIPPED: user-defined predicate detected
|
# DEBUG-NEXT: .. imm index coverage check SKIPPED: user-defined predicate detected
|
||||||
|
13
llvm/test/CodeGen/Generic/reloc-none.ll
Normal file
13
llvm/test/CodeGen/Generic/reloc-none.ll
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
; RUN: llc < %s | FileCheck %s
|
||||||
|
|
||||||
|
; CHECK: .reloc {{.*}}, BFD_RELOC_NONE, foo
|
||||||
|
|
||||||
|
%1 = type opaque
|
||||||
|
@foo = external global %1
|
||||||
|
|
||||||
|
define void @test_reloc_none() {
|
||||||
|
call void @llvm.reloc.none(ptr @foo)
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
declare void @llvm.reloc.none(ptr)
|
@ -72,11 +72,11 @@
|
|||||||
# DEBUG-NEXT: .. type index coverage check SKIPPED: user-defined predicate detected
|
# DEBUG-NEXT: .. type index coverage check SKIPPED: user-defined predicate detected
|
||||||
# DEBUG-NEXT: .. imm index coverage check SKIPPED: user-defined predicate detected
|
# DEBUG-NEXT: .. imm index coverage check SKIPPED: user-defined predicate detected
|
||||||
#
|
#
|
||||||
# DEBUG-NEXT: G_ABDS (opcode 65): 1 type index, 0 imm indices
|
# DEBUG-NEXT: G_ABDS (opcode 66): 1 type index, 0 imm indices
|
||||||
# DEBUG-NEXT:.. type index coverage check SKIPPED: no rules defined
|
# DEBUG-NEXT:.. type index coverage check SKIPPED: no rules defined
|
||||||
# DEBUG-NEXT:.. imm index coverage check SKIPPED: no rules defined
|
# DEBUG-NEXT:.. imm index coverage check SKIPPED: no rules defined
|
||||||
#
|
#
|
||||||
# DEBUG-NEXT:G_ABDU (opcode 66): 1 type index, 0 imm indices
|
# DEBUG-NEXT:G_ABDU (opcode 67): 1 type index, 0 imm indices
|
||||||
# DEBUG-NEXT:.. type index coverage check SKIPPED: no rules defined
|
# DEBUG-NEXT:.. type index coverage check SKIPPED: no rules defined
|
||||||
# DEBUG-NEXT:.. imm index coverage check SKIPPED: no rules defined
|
# DEBUG-NEXT:.. imm index coverage check SKIPPED: no rules defined
|
||||||
#
|
#
|
||||||
|
17
llvm/test/CodeGen/X86/GlobalISel/reloc-none.ll
Normal file
17
llvm/test/CodeGen/X86/GlobalISel/reloc-none.ll
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
|
||||||
|
; RUN: llc -mtriple=x86_64-linux-gnu -global-isel -verify-machineinstrs < %s -o - | FileCheck %s --check-prefix=CHECK
|
||||||
|
|
||||||
|
%1 = type opaque
|
||||||
|
@foo = external global %1
|
||||||
|
|
||||||
|
define void @test_reloc_none() {
|
||||||
|
; CHECK-LABEL: test_reloc_none:
|
||||||
|
; CHECK: # %bb.0:
|
||||||
|
; CHECK-NEXT: .Lreloc_none0:
|
||||||
|
; CHECK-NEXT: .reloc .Lreloc_none0, BFD_RELOC_NONE, foo
|
||||||
|
; CHECK-NEXT: retq
|
||||||
|
call void @llvm.reloc.none(ptr @foo)
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
declare void @llvm.reloc.none(ptr)
|
@ -15,10 +15,10 @@ class I : Instruction {
|
|||||||
// CHECK-NEXT: MCD::OPC_ExtractField, 0, 64,
|
// CHECK-NEXT: MCD::OPC_ExtractField, 0, 64,
|
||||||
// CHECK-NEXT: MCD::OPC_FilterValue, 1, 8, 0,
|
// CHECK-NEXT: MCD::OPC_FilterValue, 1, 8, 0,
|
||||||
// CHECK-NEXT: MCD::OPC_CheckFieldOrFail, 127, 1, 1,
|
// CHECK-NEXT: MCD::OPC_CheckFieldOrFail, 127, 1, 1,
|
||||||
// CHECK-NEXT: MCD::OPC_Decode, 187, 2, 0,
|
// CHECK-NEXT: MCD::OPC_Decode, 188, 2, 0,
|
||||||
// CHECK-NEXT: MCD::OPC_FilterValueOrFail, 255, 255, 255, 255, 255, 255, 255, 255, 255, 1,
|
// CHECK-NEXT: MCD::OPC_FilterValueOrFail, 255, 255, 255, 255, 255, 255, 255, 255, 255, 1,
|
||||||
// CHECK-NEXT: MCD::OPC_CheckFieldOrFail, 127, 1, 0,
|
// CHECK-NEXT: MCD::OPC_CheckFieldOrFail, 127, 1, 0,
|
||||||
// CHECK-NEXT: MCD::OPC_Decode, 186, 2, 0,
|
// CHECK-NEXT: MCD::OPC_Decode, 187, 2, 0,
|
||||||
// CHECK-NEXT: MCD::OPC_Fail,
|
// CHECK-NEXT: MCD::OPC_Fail,
|
||||||
// CHECK-NEXT: 0
|
// CHECK-NEXT: 0
|
||||||
// CHECK-NEXT: };
|
// CHECK-NEXT: };
|
||||||
|
@ -96,7 +96,7 @@ def MyCombiner: GICombiner<"GenMyCombiner", [
|
|||||||
|
|
||||||
// CHECK: const uint8_t *GenMyCombiner::getMatchTable() const {
|
// CHECK: const uint8_t *GenMyCombiner::getMatchTable() const {
|
||||||
// CHECK-NEXT: constexpr static uint8_t MatchTable0[] = {
|
// CHECK-NEXT: constexpr static uint8_t MatchTable0[] = {
|
||||||
// CHECK-NEXT: /* 0 */ GIM_SwitchOpcode, /*MI*/0, /*[*/GIMT_Encode2(99), GIMT_Encode2(210), /*)*//*default:*//*Label 5*/ GIMT_Encode4(520),
|
// CHECK-NEXT: /* 0 */ GIM_SwitchOpcode, /*MI*/0, /*[*/GIMT_Encode2(100), GIMT_Encode2(211), /*)*//*default:*//*Label 5*/ GIMT_Encode4(520),
|
||||||
// CHECK-NEXT: /* 10 */ /*TargetOpcode::G_STORE*//*Label 0*/ GIMT_Encode4(454), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0),
|
// CHECK-NEXT: /* 10 */ /*TargetOpcode::G_STORE*//*Label 0*/ GIMT_Encode4(454), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0),
|
||||||
// CHECK-NEXT: /* 182 */ /*TargetOpcode::G_SEXT*//*Label 1*/ GIMT_Encode4(472), GIMT_Encode4(0),
|
// CHECK-NEXT: /* 182 */ /*TargetOpcode::G_SEXT*//*Label 1*/ GIMT_Encode4(472), GIMT_Encode4(0),
|
||||||
// CHECK-NEXT: /* 190 */ /*TargetOpcode::G_ZEXT*//*Label 2*/ GIMT_Encode4(484), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0),
|
// CHECK-NEXT: /* 190 */ /*TargetOpcode::G_ZEXT*//*Label 2*/ GIMT_Encode4(484), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0),
|
||||||
|
13
llvm/test/Verifier/reloc-none.ll
Normal file
13
llvm/test/Verifier/reloc-none.ll
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
; RUN: not llvm-as -disable-output 2>&1 %s | FileCheck %s
|
||||||
|
|
||||||
|
; CHECK: llvm.reloc.none argument must be a global value
|
||||||
|
; CHECK-NEXT: call void @llvm.reloc.none(ptr %foo)
|
||||||
|
|
||||||
|
define void @test_reloc_none_bad_arg(ptr %foo) {
|
||||||
|
call void @llvm.reloc.none(ptr %foo)
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
declare void @llvm.reloc.none(ptr)
|
||||||
|
|
||||||
|
!0 = !{}
|
Loading…
x
Reference in New Issue
Block a user