43 changes: 33 additions & 10 deletions bolt/lib/Rewrite/DWARFRewriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -375,12 +375,11 @@ static cl::opt<bool> AlwaysConvertToRanges(
extern cl::opt<std::string> CompDirOverride;
} // namespace opts

static bool getLowAndHighPC(const DIE &Die, const DWARFUnit &DU,
uint64_t &LowPC, uint64_t &HighPC,
uint64_t &SectionIndex) {
/// If DW_AT_low_pc exists sets LowPC and returns true.
static bool getLowPC(const DIE &Die, const DWARFUnit &DU, uint64_t &LowPC,
uint64_t &SectionIndex) {
DIEValue DvalLowPc = Die.findAttribute(dwarf::DW_AT_low_pc);
DIEValue DvalHighPc = Die.findAttribute(dwarf::DW_AT_high_pc);
if (!DvalLowPc || !DvalHighPc)
if (!DvalLowPc)
return false;

dwarf::Form Form = DvalLowPc.getForm();
Expand All @@ -403,14 +402,39 @@ static bool getLowAndHighPC(const DIE &Die, const DWARFUnit &DU,
LowPC = LowPcValue;
SectionIndex = 0;
}
return true;
}

/// If DW_AT_high_pc exists sets HighPC and returns true.
static bool getHighPC(const DIE &Die, const uint64_t LowPC, uint64_t &HighPC) {
DIEValue DvalHighPc = Die.findAttribute(dwarf::DW_AT_high_pc);
if (!DvalHighPc)
return false;
if (DvalHighPc.getForm() == dwarf::DW_FORM_addr)
HighPC = DvalHighPc.getDIEInteger().getValue();
else
HighPC = LowPC + DvalHighPc.getDIEInteger().getValue();

return true;
}

/// If DW_AT_low_pc and DW_AT_high_pc exist sets LowPC and HighPC and returns
/// true.
static bool getLowAndHighPC(const DIE &Die, const DWARFUnit &DU,
uint64_t &LowPC, uint64_t &HighPC,
uint64_t &SectionIndex) {
uint64_t TempLowPC = LowPC;
uint64_t TempHighPC = HighPC;
uint64_t TempSectionIndex = SectionIndex;
if (getLowPC(Die, DU, TempLowPC, TempSectionIndex) &&
getHighPC(Die, TempLowPC, TempHighPC)) {
LowPC = TempLowPC;
HighPC = TempHighPC;
SectionIndex = TempSectionIndex;
return true;
}
return false;
}

static Expected<llvm::DWARFAddressRangesVector>
getDIEAddressRanges(const DIE &Die, DWARFUnit &DU) {
uint64_t LowPC, HighPC, Index;
Expand Down Expand Up @@ -1248,10 +1272,9 @@ void DWARFRewriter::updateUnitDebugInfo(
}
}
} else if (LowPCAttrInfo) {
const std::optional<uint64_t> Result =
LowPCAttrInfo.getDIEInteger().getValue();
if (Result.has_value()) {
const uint64_t Address = Result.value();
uint64_t Address = 0;
uint64_t SectionIndex = 0;
if (getLowPC(*Die, Unit, Address, SectionIndex)) {
uint64_t NewAddress = 0;
if (const BinaryFunction *Function =
BC.getBinaryFunctionContainingAddress(Address)) {
Expand Down
47 changes: 46 additions & 1 deletion bolt/lib/Rewrite/LinuxKernelRewriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -252,11 +252,17 @@ class LinuxKernelRewriter final : public MetadataRewriter {

/// Paravirtual instruction patch sites.
Error readParaInstructions();
Error rewriteParaInstructions();

Error readBugTable();

/// Read alternative instruction info from .altinstructions.
/// Do no process functions containing instruction annotated with
/// \p Annotation.
void skipFunctionsWithAnnotation(StringRef Annotation) const;

/// Handle alternative instruction info from .altinstructions.
Error readAltInstructions();
Error rewriteAltInstructions();

/// Read .pci_fixup
Error readPCIFixupTable();
Expand Down Expand Up @@ -318,6 +324,12 @@ class LinuxKernelRewriter final : public MetadataRewriter {
if (Error E = rewriteExceptionTable())
return E;

if (Error E = rewriteAltInstructions())
return E;

if (Error E = rewriteParaInstructions())
return E;

if (Error E = rewriteORCTables())
return E;

Expand Down Expand Up @@ -1126,6 +1138,31 @@ Error LinuxKernelRewriter::readParaInstructions() {
return Error::success();
}

void LinuxKernelRewriter::skipFunctionsWithAnnotation(
StringRef Annotation) const {
for (BinaryFunction &BF : llvm::make_second_range(BC.getBinaryFunctions())) {
if (!BC.shouldEmit(BF))
continue;
for (const BinaryBasicBlock &BB : BF) {
const bool HasAnnotation = llvm::any_of(BB, [&](const MCInst &Inst) {
return BC.MIB->hasAnnotation(Inst, Annotation);
});
if (HasAnnotation) {
BF.setSimple(false);
break;
}
}
}
}

Error LinuxKernelRewriter::rewriteParaInstructions() {
// Disable output of functions with paravirtual instructions before the
// rewrite support is complete.
skipFunctionsWithAnnotation("ParaSite");

return Error::success();
}

/// Process __bug_table section.
/// This section contains information useful for kernel debugging.
/// Each entry in the section is a struct bug_entry that contains a pointer to
Expand Down Expand Up @@ -1305,6 +1342,14 @@ Error LinuxKernelRewriter::readAltInstructions() {
return Error::success();
}

Error LinuxKernelRewriter::rewriteAltInstructions() {
// Disable output of functions with alt instructions before the rewrite
// support is complete.
skipFunctionsWithAnnotation("AltInst");

return Error::success();
}

/// When the Linux kernel needs to handle an error associated with a given PCI
/// device, it uses a table stored in .pci_fixup section to locate a fixup code
/// specific to the vendor and the problematic device. The section contains a
Expand Down
4 changes: 4 additions & 0 deletions bolt/lib/Rewrite/RewriteInstance.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ extern cl::list<std::string> HotTextMoveSections;
extern cl::opt<bool> Hugify;
extern cl::opt<bool> Instrument;
extern cl::opt<JumpTableSupportLevel> JumpTables;
extern cl::opt<bool> KeepNops;
extern cl::list<std::string> ReorderData;
extern cl::opt<bolt::ReorderFunctions::ReorderType> ReorderFunctions;
extern cl::opt<bool> TimeBuild;
Expand Down Expand Up @@ -2031,6 +2032,9 @@ void RewriteInstance::adjustCommandLineOptions() {

if (opts::Lite)
BC->outs() << "BOLT-INFO: enabling lite mode\n";

if (BC->IsLinuxKernel && !opts::KeepNops.getNumOccurrences())
opts::KeepNops = true;
}

namespace {
Expand Down
263 changes: 263 additions & 0 deletions bolt/test/X86/dwarf4-label-low-pc.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,263 @@

# REQUIRES: system-linux

# RUN: llvm-mc -dwarf-version=4 -filetype=obj -triple x86_64-unknown-linux %s -o %tmain.o
# RUN: %clang %cflags -dwarf-4 %tmain.o -o %t.exe -Wl,-q
# RUN: llvm-bolt %t.exe -o %t.bolt --update-debug-sections
# RUN: llvm-dwarfdump --show-form --verbose --debug-info %t.exe | FileCheck --check-prefix=PRECHECK %s
# RUN: llvm-dwarfdump --show-form --verbose --debug-info %t.bolt > %t.txt
# RUN: llvm-objdump -d %t.bolt >> %t.txt
# RUN: cat %t.txt | FileCheck --check-prefix=POSTCHECK %s

## This test checks that we correctly handle DW_AT_low_pc [DW_FORM_addr] that is part of DW_TAG_label.

# PRECHECK: version = 0x0004
# PRECHECK: DW_TAG_label
# PRECHECK-NEXT: DW_AT_name
# PRECHECK-NEXT: DW_AT_decl_file
# PRECHECK-NEXT: DW_AT_decl_line
# PRECHECK-NEXT:DW_AT_low_pc [DW_FORM_addr]
# PRECHECK: DW_TAG_label
# PRECHECK-NEXT: DW_AT_name
# PRECHECK-NEXT: DW_AT_decl_file
# PRECHECK-NEXT: DW_AT_decl_line
# PRECHECK-NEXT:DW_AT_low_pc [DW_FORM_addr]

# POSTCHECK: version = 0x0004
# POSTCHECK: DW_TAG_label
# POSTCHECK-NEXT: DW_AT_name
# POSTCHECK-NEXT: DW_AT_decl_file
# POSTCHECK-NEXT: DW_AT_decl_line
# POSTCHECK-NEXT:DW_AT_low_pc [DW_FORM_addr] (0x[[ADDR:[1-9a-f]*]]
# POSTCHECK: DW_TAG_label
# POSTCHECK-NEXT: DW_AT_name
# POSTCHECK-NEXT: DW_AT_decl_file
# POSTCHECK-NEXT: DW_AT_decl_line
# POSTCHECK-NEXT:DW_AT_low_pc [DW_FORM_addr] (0x[[ADDR2:[1-9a-f]*]]

# POSTCHECK: [[ADDR]]: 8b 45 f8
# POSTCHECK: [[ADDR2]]: 8b 45 f8

## clang++ main.cpp -g2 -gdwarf-4 -S
## int main() {
## int a = 4;
## if (a == 5)
## goto LABEL1;
## else
## goto LABEL2;
## LABEL1:a++;
## LABEL2:a--;
## return 0;
## }

.text
.file "main.cpp"
.globl main # -- Begin function main
.p2align 4, 0x90
.type main,@function
main: # @main
.Lfunc_begin0:
.file 1 "/home" "main.cpp"
.loc 1 1 0 # main.cpp:1:0
.cfi_startproc
# %bb.0: # %entry
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset %rbp, -16
movq %rsp, %rbp
.cfi_def_cfa_register %rbp
movl $0, -4(%rbp)
.Ltmp0:
.loc 1 2 7 prologue_end # main.cpp:2:7
movl $4, -8(%rbp)
.Ltmp1:
.loc 1 3 9 # main.cpp:3:9
cmpl $5, -8(%rbp)
.Ltmp2:
.loc 1 3 7 is_stmt 0 # main.cpp:3:7
jne .LBB0_2
# %bb.1: # %if.then
.Ltmp3:
.loc 1 4 5 is_stmt 1 # main.cpp:4:5
jmp .LBB0_3
.LBB0_2: # %if.else
.loc 1 6 5 # main.cpp:6:5
jmp .LBB0_4
.Ltmp4:
.LBB0_3: # %LABEL1
#DEBUG_LABEL: main:LABEL1
.loc 1 7 11 # main.cpp:7:11
movl -8(%rbp), %eax
addl $1, %eax
movl %eax, -8(%rbp)
.LBB0_4: # %LABEL2
.Ltmp5:
#DEBUG_LABEL: main:LABEL2
.loc 1 8 11 # main.cpp:8:11
movl -8(%rbp), %eax
addl $-1, %eax
movl %eax, -8(%rbp)
.loc 1 9 3 # main.cpp:9:3
xorl %eax, %eax
.loc 1 9 3 epilogue_begin is_stmt 0 # main.cpp:9:3
popq %rbp
.cfi_def_cfa %rsp, 8
retq
.Ltmp6:
.Lfunc_end0:
.size main, .Lfunc_end0-main
.cfi_endproc
# -- End function
.section .debug_abbrev,"",@progbits
.byte 1 # Abbreviation Code
.byte 17 # DW_TAG_compile_unit
.byte 1 # DW_CHILDREN_yes
.byte 37 # DW_AT_producer
.byte 14 # DW_FORM_strp
.byte 19 # DW_AT_language
.byte 5 # DW_FORM_data2
.byte 3 # DW_AT_name
.byte 14 # DW_FORM_strp
.byte 16 # DW_AT_stmt_list
.byte 23 # DW_FORM_sec_offset
.byte 27 # DW_AT_comp_dir
.byte 14 # DW_FORM_strp
.byte 17 # DW_AT_low_pc
.byte 1 # DW_FORM_addr
.byte 18 # DW_AT_high_pc
.byte 6 # DW_FORM_data4
.byte 0 # EOM(1)
.byte 0 # EOM(2)
.byte 2 # Abbreviation Code
.byte 46 # DW_TAG_subprogram
.byte 1 # DW_CHILDREN_yes
.byte 17 # DW_AT_low_pc
.byte 1 # DW_FORM_addr
.byte 18 # DW_AT_high_pc
.byte 6 # DW_FORM_data4
.byte 64 # DW_AT_frame_base
.byte 24 # DW_FORM_exprloc
.byte 3 # DW_AT_name
.byte 14 # DW_FORM_strp
.byte 58 # DW_AT_decl_file
.byte 11 # DW_FORM_data1
.byte 59 # DW_AT_decl_line
.byte 11 # DW_FORM_data1
.byte 73 # DW_AT_type
.byte 19 # DW_FORM_ref4
.byte 63 # DW_AT_external
.byte 25 # DW_FORM_flag_present
.byte 0 # EOM(1)
.byte 0 # EOM(2)
.byte 3 # Abbreviation Code
.byte 52 # DW_TAG_variable
.byte 0 # DW_CHILDREN_no
.byte 2 # DW_AT_location
.byte 24 # DW_FORM_exprloc
.byte 3 # DW_AT_name
.byte 14 # DW_FORM_strp
.byte 58 # DW_AT_decl_file
.byte 11 # DW_FORM_data1
.byte 59 # DW_AT_decl_line
.byte 11 # DW_FORM_data1
.byte 73 # DW_AT_type
.byte 19 # DW_FORM_ref4
.byte 0 # EOM(1)
.byte 0 # EOM(2)
.byte 4 # Abbreviation Code
.byte 10 # DW_TAG_label
.byte 0 # DW_CHILDREN_no
.byte 3 # DW_AT_name
.byte 14 # DW_FORM_strp
.byte 58 # DW_AT_decl_file
.byte 11 # DW_FORM_data1
.byte 59 # DW_AT_decl_line
.byte 11 # DW_FORM_data1
.byte 17 # DW_AT_low_pc
.byte 1 # DW_FORM_addr
.byte 0 # EOM(1)
.byte 0 # EOM(2)
.byte 5 # Abbreviation Code
.byte 36 # DW_TAG_base_type
.byte 0 # DW_CHILDREN_no
.byte 3 # DW_AT_name
.byte 14 # DW_FORM_strp
.byte 62 # DW_AT_encoding
.byte 11 # DW_FORM_data1
.byte 11 # DW_AT_byte_size
.byte 11 # DW_FORM_data1
.byte 0 # EOM(1)
.byte 0 # EOM(2)
.byte 0 # EOM(3)
.section .debug_info,"",@progbits
.Lcu_begin0:
.long .Ldebug_info_end0-.Ldebug_info_start0 # Length of Unit
.Ldebug_info_start0:
.short 4 # DWARF version number
.long .debug_abbrev # Offset Into Abbrev. Section
.byte 8 # Address Size (in bytes)
.byte 1 # Abbrev [1] 0xb:0x6d DW_TAG_compile_unit
.long .Linfo_string0 # DW_AT_producer
.short 33 # DW_AT_language
.long .Linfo_string1 # DW_AT_name
.long .Lline_table_start0 # DW_AT_stmt_list
.long .Linfo_string2 # DW_AT_comp_dir
.quad .Lfunc_begin0 # DW_AT_low_pc
.long .Lfunc_end0-.Lfunc_begin0 # DW_AT_high_pc
.byte 2 # Abbrev [2] 0x2a:0x46 DW_TAG_subprogram
.quad .Lfunc_begin0 # DW_AT_low_pc
.long .Lfunc_end0-.Lfunc_begin0 # DW_AT_high_pc
.byte 1 # DW_AT_frame_base
.byte 86
.long .Linfo_string3 # DW_AT_name
.byte 1 # DW_AT_decl_file
.byte 1 # DW_AT_decl_line
.long 112 # DW_AT_type
# DW_AT_external
.byte 3 # Abbrev [3] 0x43:0xe DW_TAG_variable
.byte 2 # DW_AT_location
.byte 145
.byte 120
.long .Linfo_string5 # DW_AT_name
.byte 1 # DW_AT_decl_file
.byte 2 # DW_AT_decl_line
.long 112 # DW_AT_type
.byte 4 # Abbrev [4] 0x51:0xf DW_TAG_label
.long .Linfo_string6 # DW_AT_name
.byte 1 # DW_AT_decl_file
.byte 7 # DW_AT_decl_line
.quad .Ltmp4 # DW_AT_low_pc
.byte 4 # Abbrev [4] 0x60:0xf DW_TAG_label
.long .Linfo_string7 # DW_AT_name
.byte 1 # DW_AT_decl_file
.byte 8 # DW_AT_decl_line
.quad .Ltmp5 # DW_AT_low_pc
.byte 0 # End Of Children Mark
.byte 5 # Abbrev [5] 0x70:0x7 DW_TAG_base_type
.long .Linfo_string4 # DW_AT_name
.byte 5 # DW_AT_encoding
.byte 4 # DW_AT_byte_size
.byte 0 # End Of Children Mark
.Ldebug_info_end0:
.section .debug_str,"MS",@progbits,1
.Linfo_string0:
.asciz "clang version 19.0.0git" # string offset=0
.Linfo_string1:
.asciz "main.cpp" # string offset=24
.Linfo_string2:
.asciz "/home" # string offset=33
.Linfo_string3:
.asciz "main" # string offset=71
.Linfo_string4:
.asciz "int" # string offset=76
.Linfo_string5:
.asciz "a" # string offset=80
.Linfo_string6:
.asciz "LABEL1" # string offset=82
.Linfo_string7:
.asciz "LABEL2" # string offset=89
.ident "clang version 19.0.0git"
.section ".note.GNU-stack","",@progbits
.addrsig
.section .debug_line,"",@progbits
.Lline_table_start0:
712 changes: 712 additions & 0 deletions bolt/test/X86/dwarf5-debug-names-cross-cu.s

Large diffs are not rendered by default.

36 changes: 20 additions & 16 deletions bolt/test/X86/dwarf5-label-low-pc.s
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@

# RUN: llvm-dwarfdump --show-form --verbose --debug-addr %t.bolt > %t.txt
# RUN: llvm-dwarfdump --show-form --verbose --debug-info %t.bolt >> %t.txt
# RUN: llvm-objdump -d %t.bolt >> %t.txt
# RUN: cat %t.txt | FileCheck --check-prefix=POSTCHECK %s

# This test checks that we correctly handle DW_AT_low_pc [DW_FORM_addrx] that is part of DW_TAG_label.
## This test checks that we correctly handle DW_AT_low_pc [DW_FORM_addrx] that is part of DW_TAG_label.

# PRECHECK: version = 0x0005
# PRECHECK: DW_TAG_label
Expand All @@ -28,34 +29,37 @@
# POSTCHECK: Addrs: [
# POSTCHECK-NEXT: 0x
# POSTCHECK-NEXT: 0x
# POSTCHECK-NEXT: 0x[[#%.16x,ADDR:]]
# POSTCHECK-NEXT: 0x[[#%.16x,ADDR2:]]
# POSTCHECK-NEXT: 0x[[ADDR:[1-9a-f]*]]
# POSTCHECK-NEXT: 0x[[ADDR2:[1-9a-f]*]]

# POSTCHECK: version = 0x0005
# POSTCHECK: DW_TAG_label
# POSTCHECK-NEXT: DW_AT_name
# POSTCHECK-NEXT: DW_AT_decl_file
# POSTCHECK-NEXT: DW_AT_decl_line
# POSTCHECK-NEXT:DW_AT_low_pc [DW_FORM_addrx] (indexed (00000002)
# POSTCHECK-SAME: 0x[[#ADDR]]
# POSTCHECK-SAME: 0x[[ADDR]]
# POSTCHECK: DW_TAG_label
# POSTCHECK-NEXT: DW_AT_name
# POSTCHECK-NEXT: DW_AT_decl_file
# POSTCHECK-NEXT: DW_AT_decl_line
# POSTCHECK-NEXT:DW_AT_low_pc [DW_FORM_addrx] (indexed (00000003)
# POSTCHECK-SAME: 0x[[#ADDR2]]
# POSTCHECK-SAME: 0x[[ADDR2]]

# clang++ main.cpp -g -S
# int main() {
# int a = 4;
# if (a == 5)
# goto LABEL1;
# else
# goto LABEL2;
# LABEL1:a++;
# LABEL2:a--;
# return 0;
# }
# POSTCHECK: [[ADDR]]: 8b 45 f8
# POSTCHECK: [[ADDR2]]: 8b 45 f8

## clang++ main.cpp -g -S
## int main() {
## int a = 4;
## if (a == 5)
## goto LABEL1;
## else
## goto LABEL2;
## LABEL1:a++;
## LABEL2:a--;
## return 0;
## }

.text
.file "main.cpp"
Expand Down
15 changes: 7 additions & 8 deletions bolt/test/X86/linux-alt-instruction.s
Original file line number Diff line number Diff line change
Expand Up @@ -6,31 +6,30 @@
# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-unknown %s -o %t.o
# RUN: %clang %cflags -nostdlib %t.o -o %t.exe \
# RUN: -Wl,--image-base=0xffffffff80000000,--no-dynamic-linker,--no-eh-frame-hdr,--no-pie
# RUN: llvm-bolt %t.exe --print-normalized --keep-nops -o %t.out \
# RUN: --alt-inst-feature-size=2 | FileCheck %s
# RUN: llvm-bolt %t.exe --print-normalized --alt-inst-feature-size=2 -o %t.out \
# RUN: | FileCheck %s

## Older kernels used to have padlen field in alt_instr. Check compatibility.

# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-unknown --defsym PADLEN=1 \
# RUN: %s -o %t.o
# RUN: %clang %cflags -nostdlib %t.o -o %t.exe \
# RUN: -Wl,--image-base=0xffffffff80000000,--no-dynamic-linker,--no-eh-frame-hdr,--no-pie
# RUN: llvm-bolt %t.exe --print-normalized --keep-nops --alt-inst-has-padlen \
# RUN: -o %t.out | FileCheck %s
# RUN: llvm-bolt %t.exe --print-normalized --alt-inst-has-padlen -o %t.out \
# RUN: | FileCheck %s

## Check with a larger size of "feature" field in alt_instr.

# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-unknown \
# RUN: --defsym FEATURE_SIZE_4=1 %s -o %t.o
# RUN: %clang %cflags -nostdlib %t.o -o %t.exe \
# RUN: -Wl,--image-base=0xffffffff80000000,--no-dynamic-linker,--no-eh-frame-hdr,--no-pie
# RUN: llvm-bolt %t.exe --print-normalized --keep-nops \
# RUN: --alt-inst-feature-size=4 -o %t.out | FileCheck %s
# RUN: llvm-bolt %t.exe --print-normalized --alt-inst-feature-size=4 -o %t.out \
# RUN: | FileCheck %s

## Check that out-of-bounds read is handled properly.

# RUN: not llvm-bolt %t.exe --print-normalized --keep-nops \
# RUN: --alt-inst-feature-size=2 -o %t.out
# RUN: not llvm-bolt %t.exe --print-normalized --alt-inst-feature-size=2 -o %t.out

# CHECK: BOLT-INFO: Linux kernel binary detected
# CHECK: BOLT-INFO: parsed 2 alternative instruction entries
Expand Down
4 changes: 2 additions & 2 deletions bolt/test/X86/linux-orc.s
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
## Verify ORC bindings to instructions.

# RUN: llvm-bolt %t.exe --print-normalized --dump-orc --print-orc -o %t.out \
# RUN: --bolt-info=0 |& FileCheck %s
# RUN: --keep-nops=0 --bolt-info=0 |& FileCheck %s


## Verify ORC bindings after rewrite.
Expand All @@ -37,7 +37,7 @@

## Verify ORC binding after rewrite when some of the functions are skipped.

# RUN: llvm-bolt %t.exe -o %t.out --skip-funcs=bar --bolt-info=0
# RUN: llvm-bolt %t.exe -o %t.out --skip-funcs=bar --bolt-info=0 --keep-nops=0
# RUN: llvm-bolt %t.out -o %t.out.1 --print-normalized --print-orc \
# RUN: |& FileCheck %s

Expand Down
2 changes: 1 addition & 1 deletion bolt/test/X86/linux-parainstructions.s
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

## Verify paravirtual bindings to instructions.

# RUN: llvm-bolt %t.exe --print-normalized -o %t.out | FileCheck %s
# RUN: llvm-bolt %t.exe --print-normalized -o %t.out --keep-nops=0 | FileCheck %s

# CHECK: BOLT-INFO: Linux kernel binary detected
# CHECK: BOLT-INFO: parsed 2 paravirtual patch sites
Expand Down
16 changes: 2 additions & 14 deletions clang/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ endif()

# Must go below project(..)
include(GNUInstallDirs)
include(GetDarwinLinkerVersion)

if(CLANG_BUILT_STANDALONE)
set(CMAKE_CXX_STANDARD 17 CACHE STRING "C++ standard to conform to")
Expand Down Expand Up @@ -346,20 +347,7 @@ endif ()
# Determine HOST_LINK_VERSION on Darwin.
set(HOST_LINK_VERSION)
if (APPLE AND NOT CMAKE_LINKER MATCHES ".*lld.*")
set(LD_V_OUTPUT)
execute_process(
COMMAND sh -c "${CMAKE_LINKER} -v 2>&1 | head -1"
RESULT_VARIABLE HAD_ERROR
OUTPUT_VARIABLE LD_V_OUTPUT
)
if (HAD_ERROR)
message(FATAL_ERROR "${CMAKE_LINKER} failed with status ${HAD_ERROR}")
endif()
if ("${LD_V_OUTPUT}" MATCHES ".*ld64-([0-9.]+).*")
string(REGEX REPLACE ".*ld64-([0-9.]+).*" "\\1" HOST_LINK_VERSION ${LD_V_OUTPUT})
elseif ("${LD_V_OUTPUT}" MATCHES "[^0-9]*([0-9.]+).*")
string(REGEX REPLACE "[^0-9]*([0-9.]+).*" "\\1" HOST_LINK_VERSION ${LD_V_OUTPUT})
endif()
get_darwin_linker_version(HOST_LINK_VERSION)
message(STATUS "Host linker version: ${HOST_LINK_VERSION}")
endif()

Expand Down
2 changes: 2 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -437,6 +437,8 @@ Bug Fixes to C++ Support
- Clang's __builtin_bit_cast will now produce a constant value for records with empty bases. See:
(#GH82383)
- Fix a crash when instantiating a lambda that captures ``this`` outside of its context. Fixes (#GH85343).
- Fix an issue where a namespace alias could be defined using a qualified name (all name components
following the first `::` were ignored).

Bug Fixes to AST Handling
^^^^^^^^^^^^^^^^^^^^^^^^^
Expand Down
16 changes: 9 additions & 7 deletions clang/include/clang/Basic/BuiltinsAMDGPU.def
Original file line number Diff line number Diff line change
Expand Up @@ -432,13 +432,15 @@ TARGET_BUILTIN(__builtin_amdgcn_s_wakeup_barrier, "vi", "n", "gfx12-insts")
TARGET_BUILTIN(__builtin_amdgcn_s_barrier_leave, "b", "n", "gfx12-insts")
TARGET_BUILTIN(__builtin_amdgcn_s_get_barrier_state, "Uii", "n", "gfx12-insts")

TARGET_BUILTIN(__builtin_amdgcn_global_load_tr_v2i32, "V2iV2i*1", "nc", "gfx12-insts,wavefrontsize32")
TARGET_BUILTIN(__builtin_amdgcn_global_load_tr_v8i16, "V8sV8s*1", "nc", "gfx12-insts,wavefrontsize32")
TARGET_BUILTIN(__builtin_amdgcn_global_load_tr_v8f16, "V8hV8h*1", "nc", "gfx12-insts,wavefrontsize32")

TARGET_BUILTIN(__builtin_amdgcn_global_load_tr_i32, "ii*1", "nc", "gfx12-insts,wavefrontsize64")
TARGET_BUILTIN(__builtin_amdgcn_global_load_tr_v4i16, "V4sV4s*1", "nc", "gfx12-insts,wavefrontsize64")
TARGET_BUILTIN(__builtin_amdgcn_global_load_tr_v4f16, "V4hV4h*1", "nc", "gfx12-insts,wavefrontsize64")
TARGET_BUILTIN(__builtin_amdgcn_global_load_tr_b64_v2i32, "V2iV2i*1", "nc", "gfx12-insts,wavefrontsize32")
TARGET_BUILTIN(__builtin_amdgcn_global_load_tr_b128_v8i16, "V8sV8s*1", "nc", "gfx12-insts,wavefrontsize32")
TARGET_BUILTIN(__builtin_amdgcn_global_load_tr_b128_v8f16, "V8hV8h*1", "nc", "gfx12-insts,wavefrontsize32")
TARGET_BUILTIN(__builtin_amdgcn_global_load_tr_b128_v8bf16, "V8yV8y*1", "nc", "gfx12-insts,wavefrontsize32")

TARGET_BUILTIN(__builtin_amdgcn_global_load_tr_b64_i32, "ii*1", "nc", "gfx12-insts,wavefrontsize64")
TARGET_BUILTIN(__builtin_amdgcn_global_load_tr_b128_v4i16, "V4sV4s*1", "nc", "gfx12-insts,wavefrontsize64")
TARGET_BUILTIN(__builtin_amdgcn_global_load_tr_b128_v4f16, "V4hV4h*1", "nc", "gfx12-insts,wavefrontsize64")
TARGET_BUILTIN(__builtin_amdgcn_global_load_tr_b128_v4bf16, "V4yV4y*1", "nc", "gfx12-insts,wavefrontsize64")

//===----------------------------------------------------------------------===//
// WMMA builtins.
Expand Down
2 changes: 2 additions & 0 deletions clang/include/clang/Basic/DiagnosticParseKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,8 @@ def err_expected_semi_after_namespace_name : Error<
"expected ';' after namespace name">;
def err_unexpected_namespace_attributes_alias : Error<
"attributes cannot be specified on namespace alias">;
def err_unexpected_qualified_namespace_alias : Error<
"namespace alias must be a single identifier">;
def err_unexpected_nested_namespace_attribute : Error<
"attributes cannot be specified on a nested namespace definition">;
def err_inline_namespace_alias : Error<"namespace alias cannot be inline">;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,8 @@ class EntryRef {
/// The underlying cached entry.
const CachedFileSystemEntry &Entry;

friend class DependencyScanningWorkerFilesystem;

public:
EntryRef(StringRef Name, const CachedFileSystemEntry &Entry)
: Filename(Name), Entry(Entry) {}
Expand Down Expand Up @@ -300,14 +302,15 @@ class DependencyScanningWorkerFilesystem
///
/// Attempts to use the local and shared caches first, then falls back to
/// using the underlying filesystem.
llvm::ErrorOr<EntryRef>
getOrCreateFileSystemEntry(StringRef Filename,
bool DisableDirectivesScanning = false);
llvm::ErrorOr<EntryRef> getOrCreateFileSystemEntry(StringRef Filename);

private:
/// Check whether the file should be scanned for preprocessor directives.
bool shouldScanForDirectives(StringRef Filename);
/// Ensure the directive tokens are populated for this file entry.
///
/// Returns true if the directive tokens are populated for this file entry,
/// false if not (i.e. this entry is not a file or its scan fails).
bool ensureDirectiveTokensArePopulated(EntryRef Entry);

private:
/// For a filename that's not yet associated with any entry in the caches,
/// uses the underlying filesystem to either look up the entry based in the
/// shared cache indexed by unique ID, or creates new entry from scratch.
Expand All @@ -317,11 +320,6 @@ class DependencyScanningWorkerFilesystem
computeAndStoreResult(StringRef OriginalFilename,
StringRef FilenameForLookup);

/// Scan for preprocessor directives for the given entry if necessary and
/// returns a wrapper object with reference semantics.
EntryRef scanForDirectivesIfNecessary(const CachedFileSystemEntry &Entry,
StringRef Filename, bool Disable);

/// Represents a filesystem entry that has been stat-ed (and potentially read)
/// and that's about to be inserted into the cache as `CachedFileSystemEntry`.
struct TentativeEntry {
Expand Down
34 changes: 22 additions & 12 deletions clang/lib/CodeGen/CGBuiltin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18531,35 +18531,45 @@ Value *CodeGenFunction::EmitAMDGPUBuiltinExpr(unsigned BuiltinID,
llvm::Function *F = CGM.getIntrinsic(IID, {ArgTy});
return Builder.CreateCall(F, {Addr, Val, ZeroI32, ZeroI32, ZeroI1});
}
case AMDGPU::BI__builtin_amdgcn_global_load_tr_i32:
case AMDGPU::BI__builtin_amdgcn_global_load_tr_v2i32:
case AMDGPU::BI__builtin_amdgcn_global_load_tr_v4f16:
case AMDGPU::BI__builtin_amdgcn_global_load_tr_v4i16:
case AMDGPU::BI__builtin_amdgcn_global_load_tr_v8f16:
case AMDGPU::BI__builtin_amdgcn_global_load_tr_v8i16: {
case AMDGPU::BI__builtin_amdgcn_global_load_tr_b64_i32:
case AMDGPU::BI__builtin_amdgcn_global_load_tr_b64_v2i32:
case AMDGPU::BI__builtin_amdgcn_global_load_tr_b128_v4bf16:
case AMDGPU::BI__builtin_amdgcn_global_load_tr_b128_v4f16:
case AMDGPU::BI__builtin_amdgcn_global_load_tr_b128_v4i16:
case AMDGPU::BI__builtin_amdgcn_global_load_tr_b128_v8bf16:
case AMDGPU::BI__builtin_amdgcn_global_load_tr_b128_v8f16:
case AMDGPU::BI__builtin_amdgcn_global_load_tr_b128_v8i16: {

llvm::Type *ArgTy;
switch (BuiltinID) {
case AMDGPU::BI__builtin_amdgcn_global_load_tr_i32:
case AMDGPU::BI__builtin_amdgcn_global_load_tr_b64_i32:
ArgTy = llvm::Type::getInt32Ty(getLLVMContext());
break;
case AMDGPU::BI__builtin_amdgcn_global_load_tr_v2i32:
case AMDGPU::BI__builtin_amdgcn_global_load_tr_b64_v2i32:
ArgTy = llvm::FixedVectorType::get(
llvm::Type::getInt32Ty(getLLVMContext()), 2);
break;
case AMDGPU::BI__builtin_amdgcn_global_load_tr_v4f16:
case AMDGPU::BI__builtin_amdgcn_global_load_tr_b128_v4bf16:
ArgTy = llvm::FixedVectorType::get(
llvm::Type::getBFloatTy(getLLVMContext()), 4);
break;
case AMDGPU::BI__builtin_amdgcn_global_load_tr_b128_v4f16:
ArgTy = llvm::FixedVectorType::get(
llvm::Type::getHalfTy(getLLVMContext()), 4);
break;
case AMDGPU::BI__builtin_amdgcn_global_load_tr_v4i16:
case AMDGPU::BI__builtin_amdgcn_global_load_tr_b128_v4i16:
ArgTy = llvm::FixedVectorType::get(
llvm::Type::getInt16Ty(getLLVMContext()), 4);
break;
case AMDGPU::BI__builtin_amdgcn_global_load_tr_v8f16:
case AMDGPU::BI__builtin_amdgcn_global_load_tr_b128_v8bf16:
ArgTy = llvm::FixedVectorType::get(
llvm::Type::getBFloatTy(getLLVMContext()), 8);
break;
case AMDGPU::BI__builtin_amdgcn_global_load_tr_b128_v8f16:
ArgTy = llvm::FixedVectorType::get(
llvm::Type::getHalfTy(getLLVMContext()), 8);
break;
case AMDGPU::BI__builtin_amdgcn_global_load_tr_v8i16:
case AMDGPU::BI__builtin_amdgcn_global_load_tr_b128_v8i16:
ArgTy = llvm::FixedVectorType::get(
llvm::Type::getInt16Ty(getLLVMContext()), 8);
break;
Expand Down
27 changes: 9 additions & 18 deletions clang/lib/CodeGen/CGCUDANV.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -605,20 +605,10 @@ llvm::Function *CGNVCUDARuntime::makeRegisterGlobalsFn() {
uint64_t VarSize =
CGM.getDataLayout().getTypeAllocSize(Var->getValueType());
if (Info.Flags.isManaged()) {
auto *ManagedVar = new llvm::GlobalVariable(
CGM.getModule(), Var->getType(),
/*isConstant=*/false, Var->getLinkage(),
/*Init=*/Var->isDeclaration()
? nullptr
: llvm::ConstantPointerNull::get(Var->getType()),
/*Name=*/"", /*InsertBefore=*/nullptr,
llvm::GlobalVariable::NotThreadLocal);
ManagedVar->setDSOLocal(Var->isDSOLocal());
ManagedVar->setVisibility(Var->getVisibility());
ManagedVar->setExternallyInitialized(true);
ManagedVar->takeName(Var);
Var->setName(Twine(ManagedVar->getName() + ".managed"));
replaceManagedVar(Var, ManagedVar);
assert(Var->getName().ends_with(".managed") &&
"HIP managed variables not transformed");
auto *ManagedVar = CGM.getModule().getNamedGlobal(
Var->getName().drop_back(StringRef(".managed").size()));
llvm::Value *Args[] = {
&GpuBinaryHandlePtr,
ManagedVar,
Expand Down Expand Up @@ -1093,7 +1083,9 @@ void CGNVCUDARuntime::transformManagedVars() {
: llvm::ConstantPointerNull::get(Var->getType()),
/*Name=*/"", /*InsertBefore=*/nullptr,
llvm::GlobalVariable::NotThreadLocal,
CGM.getContext().getTargetAddressSpace(LangAS::cuda_device));
CGM.getContext().getTargetAddressSpace(CGM.getLangOpts().CUDAIsDevice
? LangAS::cuda_device
: LangAS::Default));
ManagedVar->setDSOLocal(Var->isDSOLocal());
ManagedVar->setVisibility(Var->getVisibility());
ManagedVar->setExternallyInitialized(true);
Expand All @@ -1102,7 +1094,7 @@ void CGNVCUDARuntime::transformManagedVars() {
Var->setName(Twine(ManagedVar->getName()) + ".managed");
// Keep managed variables even if they are not used in device code since
// they need to be allocated by the runtime.
if (!Var->isDeclaration()) {
if (CGM.getLangOpts().CUDAIsDevice && !Var->isDeclaration()) {
assert(!ManagedVar->isDeclaration());
CGM.addCompilerUsedGlobal(Var);
CGM.addCompilerUsedGlobal(ManagedVar);
Expand Down Expand Up @@ -1160,9 +1152,8 @@ void CGNVCUDARuntime::createOffloadingEntries() {

// Returns module constructor to be added.
llvm::Function *CGNVCUDARuntime::finalizeModule() {
transformManagedVars();
if (CGM.getLangOpts().CUDAIsDevice) {
transformManagedVars();

// Mark ODR-used device variables as compiler used to prevent it from being
// eliminated by optimization. This is necessary for device variables
// ODR-used by host functions. Sema correctly marks them as ODR-used no
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/CodeGen/CGDebugInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3463,6 +3463,9 @@ static QualType UnwrapTypeForDebugInfo(QualType T, const ASTContext &C) {
case Type::BTFTagAttributed:
T = cast<BTFTagAttributedType>(T)->getWrappedType();
break;
case Type::CountAttributed:
T = cast<CountAttributedType>(T)->desugar();
break;
case Type::Elaborated:
T = cast<ElaboratedType>(T)->getNamedType();
break;
Expand Down
8 changes: 8 additions & 0 deletions clang/lib/Parse/ParseDeclCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,14 @@ Parser::DeclGroupPtrTy Parser::ParseNamespace(DeclaratorContext Context,
SkipUntil(tok::semi);
return nullptr;
}
if (!ExtraNSs.empty()) {
Diag(ExtraNSs.front().NamespaceLoc,
diag::err_unexpected_qualified_namespace_alias)
<< SourceRange(ExtraNSs.front().NamespaceLoc,
ExtraNSs.back().IdentLoc);
SkipUntil(tok::semi);
return nullptr;
}
if (attrLoc.isValid())
Diag(attrLoc, diag::err_unexpected_namespace_attributes_alias);
if (InlineLoc.isValid())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,24 +41,25 @@ DependencyScanningWorkerFilesystem::readFile(StringRef Filename) {
return TentativeEntry(Stat, std::move(Buffer));
}

EntryRef DependencyScanningWorkerFilesystem::scanForDirectivesIfNecessary(
const CachedFileSystemEntry &Entry, StringRef Filename, bool Disable) {
if (Entry.isError() || Entry.isDirectory() || Disable ||
!shouldScanForDirectives(Filename))
return EntryRef(Filename, Entry);
bool DependencyScanningWorkerFilesystem::ensureDirectiveTokensArePopulated(
EntryRef Ref) {
auto &Entry = Ref.Entry;

if (Entry.isError() || Entry.isDirectory())
return false;

CachedFileContents *Contents = Entry.getCachedContents();
assert(Contents && "contents not initialized");

// Double-checked locking.
if (Contents->DepDirectives.load())
return EntryRef(Filename, Entry);
return true;

std::lock_guard<std::mutex> GuardLock(Contents->ValueLock);

// Double-checked locking.
if (Contents->DepDirectives.load())
return EntryRef(Filename, Entry);
return true;

SmallVector<dependency_directives_scan::Directive, 64> Directives;
// Scan the file for preprocessor directives that might affect the
Expand All @@ -69,16 +70,16 @@ EntryRef DependencyScanningWorkerFilesystem::scanForDirectivesIfNecessary(
Contents->DepDirectiveTokens.clear();
// FIXME: Propagate the diagnostic if desired by the client.
Contents->DepDirectives.store(new std::optional<DependencyDirectivesTy>());
return EntryRef(Filename, Entry);
return false;
}

// This function performed double-checked locking using `DepDirectives`.
// Assigning it must be the last thing this function does, otherwise other
// threads may skip the
// critical section (`DepDirectives != nullptr`), leading to a data race.
// threads may skip the critical section (`DepDirectives != nullptr`), leading
// to a data race.
Contents->DepDirectives.store(
new std::optional<DependencyDirectivesTy>(std::move(Directives)));
return EntryRef(Filename, Entry);
return true;
}

DependencyScanningFilesystemSharedCache::
Expand Down Expand Up @@ -161,34 +162,11 @@ DependencyScanningFilesystemSharedCache::CacheShard::
return *EntriesByFilename.insert({Filename, &Entry}).first->getValue();
}

/// Whitelist file extensions that should be minimized, treating no extension as
/// a source file that should be minimized.
///
/// This is kinda hacky, it would be better if we knew what kind of file Clang
/// was expecting instead.
static bool shouldScanForDirectivesBasedOnExtension(StringRef Filename) {
StringRef Ext = llvm::sys::path::extension(Filename);
if (Ext.empty())
return true; // C++ standard library
return llvm::StringSwitch<bool>(Ext)
.CasesLower(".c", ".cc", ".cpp", ".c++", ".cxx", true)
.CasesLower(".h", ".hh", ".hpp", ".h++", ".hxx", true)
.CasesLower(".m", ".mm", true)
.CasesLower(".i", ".ii", ".mi", ".mmi", true)
.CasesLower(".def", ".inc", true)
.Default(false);
}

static bool shouldCacheStatFailures(StringRef Filename) {
StringRef Ext = llvm::sys::path::extension(Filename);
if (Ext.empty())
return false; // This may be the module cache directory.
// Only cache stat failures on files that are not expected to change during
// the build.
StringRef FName = llvm::sys::path::filename(Filename);
if (FName == "module.modulemap" || FName == "module.map")
return true;
return shouldScanForDirectivesBasedOnExtension(Filename);
return true;
}

DependencyScanningWorkerFilesystem::DependencyScanningWorkerFilesystem(
Expand All @@ -201,11 +179,6 @@ DependencyScanningWorkerFilesystem::DependencyScanningWorkerFilesystem(
updateWorkingDirForCacheLookup();
}

bool DependencyScanningWorkerFilesystem::shouldScanForDirectives(
StringRef Filename) {
return shouldScanForDirectivesBasedOnExtension(Filename);
}

const CachedFileSystemEntry &
DependencyScanningWorkerFilesystem::getOrEmplaceSharedEntryForUID(
TentativeEntry TEntry) {
Expand Down Expand Up @@ -259,7 +232,7 @@ DependencyScanningWorkerFilesystem::computeAndStoreResult(

llvm::ErrorOr<EntryRef>
DependencyScanningWorkerFilesystem::getOrCreateFileSystemEntry(
StringRef OriginalFilename, bool DisableDirectivesScanning) {
StringRef OriginalFilename) {
StringRef FilenameForLookup;
SmallString<256> PathBuf;
if (llvm::sys::path::is_absolute_gnu(OriginalFilename)) {
Expand All @@ -276,15 +249,11 @@ DependencyScanningWorkerFilesystem::getOrCreateFileSystemEntry(
assert(llvm::sys::path::is_absolute_gnu(FilenameForLookup));
if (const auto *Entry =
findEntryByFilenameWithWriteThrough(FilenameForLookup))
return scanForDirectivesIfNecessary(*Entry, OriginalFilename,
DisableDirectivesScanning)
.unwrapError();
return EntryRef(OriginalFilename, *Entry).unwrapError();
auto MaybeEntry = computeAndStoreResult(OriginalFilename, FilenameForLookup);
if (!MaybeEntry)
return MaybeEntry.getError();
return scanForDirectivesIfNecessary(*MaybeEntry, OriginalFilename,
DisableDirectivesScanning)
.unwrapError();
return EntryRef(OriginalFilename, *MaybeEntry).unwrapError();
}

llvm::ErrorOr<llvm::vfs::Status>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -372,7 +372,8 @@ class DependencyScanningAction : public tooling::ToolAction {
-> std::optional<ArrayRef<dependency_directives_scan::Directive>> {
if (llvm::ErrorOr<EntryRef> Entry =
LocalDepFS->getOrCreateFileSystemEntry(File.getName()))
return Entry->getDirectiveTokens();
if (LocalDepFS->ensureDirectiveTokensArePopulated(*Entry))
return Entry->getDirectiveTokens();
return std::nullopt;
};
}
Expand Down
60 changes: 60 additions & 0 deletions clang/test/C/C11/n1365.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// RUN: %clang_cc1 -ast-dump %s | FileCheck %s

/* WG14 N1365: Clang 16
* Constant expressions
*/

// Note: we don't allow you to expand __FLT_EVAL_METHOD__ in the presence of a
// pragma that changes its value. However, we can test that we have the correct
// constant expression behavior by testing that the AST has the correct implicit
// casts, which also specify that the cast was inserted due to an evaluation
// method requirement.
void func(void) {
{
#pragma clang fp eval_method(double)
_Static_assert(123.0F * 2.0F == 246.0F, "");
// CHECK: StaticAssertDecl
// CHECK-NEXT: ImplicitCastExpr {{.*}} '_Bool' <IntegralToBoolean>
// CHECK-NEXT: BinaryOperator {{.*}} 'int' '=='
// CHECK-NEXT: BinaryOperator {{.*}} 'double' '*' FPEvalMethod=1
// CHECK-NEXT: ImplicitCastExpr {{.*}} 'double' <FloatingCast> FPEvalMethod=1
// CHECK-NEXT: FloatingLiteral
// CHECK-NEXT: ImplicitCastExpr {{.*}} 'double' <FloatingCast> FPEvalMethod=1
// CHECK-NEXT: FloatingLiteral

// Ensure that a cast removes the extra precision.
_Static_assert(123.0F * 2.0F == 246.0F, "");
// CHECK: StaticAssertDecl
// CHECK-NEXT: ImplicitCastExpr {{.*}} '_Bool' <IntegralToBoolean>
// CHECK-NEXT: BinaryOperator {{.*}} 'int' '=='
// CHECK-NEXT: BinaryOperator {{.*}} 'double' '*' FPEvalMethod=1
// CHECK-NEXT: ImplicitCastExpr {{.*}} 'double' <FloatingCast> FPEvalMethod=1
// CHECK-NEXT: FloatingLiteral
// CHECK-NEXT: ImplicitCastExpr {{.*}} 'double' <FloatingCast> FPEvalMethod=1
// CHECK-NEXT: FloatingLiteral
}

{
#pragma clang fp eval_method(extended)
_Static_assert(123.0F * 2.0F == 246.0F, "");
// CHECK: StaticAssertDecl
// CHECK-NEXT: ImplicitCastExpr {{.*}} '_Bool' <IntegralToBoolean>
// CHECK-NEXT: BinaryOperator {{.*}} 'int' '=='
// CHECK-NEXT: BinaryOperator {{.*}} 'long double' '*' FPEvalMethod=2
// CHECK-NEXT: ImplicitCastExpr {{.*}} 'long double' <FloatingCast> FPEvalMethod=2
// CHECK-NEXT: FloatingLiteral
// CHECK-NEXT: ImplicitCastExpr {{.*}} 'long double' <FloatingCast> FPEvalMethod=2
// CHECK-NEXT: FloatingLiteral
}

{
#pragma clang fp eval_method(source)
_Static_assert(123.0F * 2.0F == 246.0F, "");
// CHECK: StaticAssertDecl
// CHECK-NEXT: ImplicitCastExpr {{.*}} '_Bool' <IntegralToBoolean>
// CHECK-NEXT: BinaryOperator {{.*}} 'int' '=='
// CHECK-NEXT: BinaryOperator {{.*}} 'float' '*' FPEvalMethod=0
// CHECK-NEXT: FloatingLiteral
// CHECK-NEXT: FloatingLiteral
}
}
20 changes: 20 additions & 0 deletions clang/test/C/drs/dr290.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/* RUN: %clang_cc1 -fsyntax-only -ast-dump %s | FileCheck %s
*/

/* WG14 DR290: no
* FLT_EVAL_METHOD and extra precision and/or range
*
* We retain an implicit conversion based on the float eval method being used
* instead of dropping it due to the explicit cast. See GH86304 and C23 6.5.5p7.
*/

#pragma clang fp eval_method(double)
_Static_assert((float)(123.0F * 2.0F) == (float)246.0F, "");

// CHECK: StaticAssertDecl
// CHECK-NEXT: ImplicitCastExpr {{.*}} '_Bool' <IntegralToBoolean>
// CHECK-NEXT: BinaryOperator {{.*}} 'int' '=='
// NB: the following implicit cast is incorrect.
// CHECK-NEXT: ImplicitCastExpr {{.*}} 'double' <FloatingCast> FPEvalMethod=1
// CHECK-NEXT: CStyleCastExpr {{.*}} 'float' <FloatingCast> FPEvalMethod=1

33 changes: 33 additions & 0 deletions clang/test/ClangScanDeps/modules-extension.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// RUN: rm -rf %t
// RUN: split-file %s %t

// This test checks that source files with uncommon extensions still undergo
// dependency directives scan. If header.pch would not and b.h would, the scan
// would fail when parsing `void function(B)` and not knowing the symbol B.

//--- module.modulemap
module __PCH { header "header.pch" }
module B { header "b.h" }

//--- header.pch
#include "b.h"
void function(B);

//--- b.h
typedef int B;

//--- tu.c
int main() {
function(0);
return 0;
}

//--- cdb.json.in
[{
"directory": "DIR",
"file": "DIR/tu.c",
"command": "clang -c DIR/tu.c -fmodules -fmodules-cache-path=DIR/cache -fimplicit-module-maps -include DIR/header.pch"
}]

// RUN: sed -e "s|DIR|%/t|g" %t/cdb.json.in > %t/cdb.json
// RUN: clang-scan-deps -compilation-database %t/cdb.json -format experimental-full > %t/deps.json
18 changes: 18 additions & 0 deletions clang/test/CodeGen/attr-counted-by-debug-info.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// RUN: %clang -emit-llvm -DCOUNTED_BY -S -g %s -o - | FileCheck %s
// RUN: %clang -emit-llvm -S -g %s -o - | FileCheck %s

#ifdef COUNTED_BY
#define __counted_by(member) __attribute__((__counted_by__(member)))
#else
#define __counted_by(member)
#endif

struct {
int num_counters;
long value[] __counted_by(num_counters);
} agent_send_response_port_num;

// CHECK: !DICompositeType(tag: DW_TAG_array_type, baseType: ![[BT:.*]], elements: ![[ELEMENTS:.*]])
// CHECK: ![[BT]] = !DIBasicType(name: "long", size: {{.*}}, encoding: DW_ATE_signed)
// CHECK: ![[ELEMENTS]] = !{![[COUNT:.*]]}
// CHECK: ![[COUNT]] = !DISubrange(count: -1)
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,22 @@
typedef int v2i __attribute__((ext_vector_type(2)));
typedef half v8h __attribute__((ext_vector_type(8)));
typedef short v8s __attribute__((ext_vector_type(8)));
typedef __bf16 v8bf16 __attribute__((ext_vector_type(8)));

typedef half v4h __attribute__((ext_vector_type(4)));
typedef short v4s __attribute__((ext_vector_type(4)));
typedef __bf16 v4bf16 __attribute__((ext_vector_type(4)));



void amdgcn_global_load_tr(global v2i* v2i_inptr, global v8s* v8s_inptr, global v8h* v8h_inptr,
global int* int_inptr, global v4s* v4s_inptr, global v4h* v4h_inptr)
void amdgcn_global_load_tr(global v2i* v2i_inptr, global v8s* v8s_inptr, global v8h* v8h_inptr, global v8bf16* v8bf16_inptr,
global int* int_inptr, global v4s* v4s_inptr, global v4h* v4h_inptr, global v4bf16* v4bf16_inptr)
{
v2i out_1 = __builtin_amdgcn_global_load_tr_v2i32(v2i_inptr); // expected-error{{'__builtin_amdgcn_global_load_tr_v2i32' needs target feature gfx12-insts,wavefrontsize32}}
v8s out_2 = __builtin_amdgcn_global_load_tr_v8i16(v8s_inptr); // expected-error{{'__builtin_amdgcn_global_load_tr_v8i16' needs target feature gfx12-insts,wavefrontsize32}}
v8h out_3 = __builtin_amdgcn_global_load_tr_v8f16(v8h_inptr); // expected-error{{'__builtin_amdgcn_global_load_tr_v8f16' needs target feature gfx12-insts,wavefrontsize32}}

int out_4 = __builtin_amdgcn_global_load_tr_i32(int_inptr); // expected-error{{'__builtin_amdgcn_global_load_tr_i32' needs target feature gfx12-insts,wavefrontsize64}}
v4s out_5 = __builtin_amdgcn_global_load_tr_v4i16(v4s_inptr); // expected-error{{'__builtin_amdgcn_global_load_tr_v4i16' needs target feature gfx12-insts,wavefrontsize64}}
v4h out_6 = __builtin_amdgcn_global_load_tr_v4f16(v4h_inptr); // expected-error{{'__builtin_amdgcn_global_load_tr_v4f16' needs target feature gfx12-insts,wavefrontsize64}}
v2i out_1 = __builtin_amdgcn_global_load_tr_b64_v2i32(v2i_inptr); // expected-error{{'__builtin_amdgcn_global_load_tr_b64_v2i32' needs target feature gfx12-insts,wavefrontsize32}}
v8s out_2 = __builtin_amdgcn_global_load_tr_b128_v8i16(v8s_inptr); // expected-error{{'__builtin_amdgcn_global_load_tr_b128_v8i16' needs target feature gfx12-insts,wavefrontsize32}}
v8h out_3 = __builtin_amdgcn_global_load_tr_b128_v8f16(v8h_inptr); // expected-error{{'__builtin_amdgcn_global_load_tr_b128_v8f16' needs target feature gfx12-insts,wavefrontsize32}}
v8bf16 o4 = __builtin_amdgcn_global_load_tr_b128_v8bf16(v8bf16_inptr); // expected-error{{'__builtin_amdgcn_global_load_tr_b128_v8bf16' needs target feature gfx12-insts,wavefrontsize32}}

int out_5 = __builtin_amdgcn_global_load_tr_b64_i32(int_inptr); // expected-error{{'__builtin_amdgcn_global_load_tr_b64_i32' needs target feature gfx12-insts,wavefrontsize64}}
v4s out_6 = __builtin_amdgcn_global_load_tr_b128_v4i16(v4s_inptr); // expected-error{{'__builtin_amdgcn_global_load_tr_b128_v4i16' needs target feature gfx12-insts,wavefrontsize64}}
v4h out_7 = __builtin_amdgcn_global_load_tr_b128_v4f16(v4h_inptr); // expected-error{{'__builtin_amdgcn_global_load_tr_b128_v4f16' needs target feature gfx12-insts,wavefrontsize64}}
v4bf16 o8 = __builtin_amdgcn_global_load_tr_b128_v4bf16(v4bf16_inptr); // expected-error{{'__builtin_amdgcn_global_load_tr_b128_v4bf16' needs target feature gfx12-insts,wavefrontsize64}}
}

Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@

typedef half v4h __attribute__((ext_vector_type(4)));
typedef short v4s __attribute__((ext_vector_type(4)));
typedef __bf16 v4bf16 __attribute__((ext_vector_type(4)));

void amdgcn_global_load_tr(global int* int_inptr, global v4s* v4s_inptr, global v4h* v4h_inptr)
void amdgcn_global_load_tr(global int* int_inptr, global v4s* v4s_inptr, global v4h* v4h_inptr, global v4bf16* v4bf16_inptr)
{
int out_4 = __builtin_amdgcn_global_load_tr_i32(int_inptr); // expected-error{{'__builtin_amdgcn_global_load_tr_i32' needs target feature gfx12-insts,wavefrontsize64}}
v4s out_5 = __builtin_amdgcn_global_load_tr_v4i16(v4s_inptr); // expected-error{{'__builtin_amdgcn_global_load_tr_v4i16' needs target feature gfx12-insts,wavefrontsize64}}
v4h out_6 = __builtin_amdgcn_global_load_tr_v4f16(v4h_inptr); // expected-error{{'__builtin_amdgcn_global_load_tr_v4f16' needs target feature gfx12-insts,wavefrontsize64}}
int out_1 = __builtin_amdgcn_global_load_tr_b64_i32(int_inptr); // expected-error{{'__builtin_amdgcn_global_load_tr_b64_i32' needs target feature gfx12-insts,wavefrontsize64}}
v4s out_2 = __builtin_amdgcn_global_load_tr_b128_v4i16(v4s_inptr); // expected-error{{'__builtin_amdgcn_global_load_tr_b128_v4i16' needs target feature gfx12-insts,wavefrontsize64}}
v4h out_3 = __builtin_amdgcn_global_load_tr_b128_v4f16(v4h_inptr); // expected-error{{'__builtin_amdgcn_global_load_tr_b128_v4f16' needs target feature gfx12-insts,wavefrontsize64}}
v4bf16 o4 = __builtin_amdgcn_global_load_tr_b128_v4bf16(v4bf16_inptr); // expected-error{{'__builtin_amdgcn_global_load_tr_b128_v4bf16' needs target feature gfx12-insts,wavefrontsize64}}
}

Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@
typedef int v2i __attribute__((ext_vector_type(2)));
typedef half v8h __attribute__((ext_vector_type(8)));
typedef short v8s __attribute__((ext_vector_type(8)));
typedef __bf16 v8bf16 __attribute__((ext_vector_type(8)));

void amdgcn_global_load_tr(global v2i* v2i_inptr, global v8s* v8s_inptr, global v8h* v8h_inptr)
void amdgcn_global_load_tr(global v2i* v2i_inptr, global v8s* v8s_inptr, global v8h* v8h_inptr, global v8bf16* v8bf16_inptr)
{
v2i out_1 = __builtin_amdgcn_global_load_tr_v2i32(v2i_inptr); // expected-error{{'__builtin_amdgcn_global_load_tr_v2i32' needs target feature gfx12-insts,wavefrontsize32}}
v8s out_2 = __builtin_amdgcn_global_load_tr_v8i16(v8s_inptr); // expected-error{{'__builtin_amdgcn_global_load_tr_v8i16' needs target feature gfx12-insts,wavefrontsize32}}
v8h out_3 = __builtin_amdgcn_global_load_tr_v8f16(v8h_inptr); // expected-error{{'__builtin_amdgcn_global_load_tr_v8f16' needs target feature gfx12-insts,wavefrontsize32}}
v2i out_1 = __builtin_amdgcn_global_load_tr_b64_v2i32(v2i_inptr); // expected-error{{'__builtin_amdgcn_global_load_tr_b64_v2i32' needs target feature gfx12-insts,wavefrontsize32}}
v8s out_2 = __builtin_amdgcn_global_load_tr_b128_v8i16(v8s_inptr); // expected-error{{'__builtin_amdgcn_global_load_tr_b128_v8i16' needs target feature gfx12-insts,wavefrontsize32}}
v8h out_3 = __builtin_amdgcn_global_load_tr_b128_v8f16(v8h_inptr); // expected-error{{'__builtin_amdgcn_global_load_tr_b128_v8f16' needs target feature gfx12-insts,wavefrontsize32}}
v8bf16 o4 = __builtin_amdgcn_global_load_tr_b128_v8bf16(v8bf16_inptr); // expected-error{{'__builtin_amdgcn_global_load_tr_b128_v8bf16' needs target feature gfx12-insts,wavefrontsize32}}
}

38 changes: 19 additions & 19 deletions clang/test/CodeGenOpenCL/builtins-amdgcn-global-load-tr-w32.cl
Original file line number Diff line number Diff line change
Expand Up @@ -5,44 +5,44 @@
typedef int v2i __attribute__((ext_vector_type(2)));
typedef half v8h __attribute__((ext_vector_type(8)));
typedef short v8s __attribute__((ext_vector_type(8)));
typedef __bf16 v8bf16 __attribute__((ext_vector_type(8)));

// Wave32

//
// amdgcn_global_load_tr
//

// CHECK-GFX1200-LABEL: @test_amdgcn_global_load_tr_v2i32(
// CHECK-GFX1200-LABEL: @test_amdgcn_global_load_tr_b64_v2i32(
// CHECK-GFX1200-NEXT: entry:
// CHECK-GFX1200-NEXT: [[TMP0:%.*]] = tail call <2 x i32> @llvm.amdgcn.global.load.tr.v2i32(ptr addrspace(1) [[INPTR:%.*]])
// CHECK-GFX1200-NEXT: ret <2 x i32> [[TMP0]]
//
v2i test_amdgcn_global_load_tr_v2i32(global v2i* inptr)
v2i test_amdgcn_global_load_tr_b64_v2i32(global v2i* inptr)
{
return __builtin_amdgcn_global_load_tr_v2i32(inptr);
return __builtin_amdgcn_global_load_tr_b64_v2i32(inptr);
}

//
// amdgcn_global_load_tr
//

// CHECK-GFX1200-LABEL: @test_amdgcn_global_load_tr_v8i16(
// CHECK-GFX1200-LABEL: @test_amdgcn_global_load_tr_b128_v8i16(
// CHECK-GFX1200-NEXT: entry:
// CHECK-GFX1200-NEXT: [[TMP0:%.*]] = tail call <8 x i16> @llvm.amdgcn.global.load.tr.v8i16(ptr addrspace(1) [[INPTR:%.*]])
// CHECK-GFX1200-NEXT: ret <8 x i16> [[TMP0]]
//
v8s test_amdgcn_global_load_tr_v8i16(global v8s* inptr)
v8s test_amdgcn_global_load_tr_b128_v8i16(global v8s* inptr)
{
return __builtin_amdgcn_global_load_tr_v8i16(inptr);
return __builtin_amdgcn_global_load_tr_b128_v8i16(inptr);
}

// CHECK-GFX1200-LABEL: @test_amdgcn_global_load_tr_v8f16(
// CHECK-GFX1200-LABEL: @test_amdgcn_global_load_tr_b128_v8f16(
// CHECK-GFX1200-NEXT: entry:
// CHECK-GFX1200-NEXT: [[TMP0:%.*]] = tail call <8 x half> @llvm.amdgcn.global.load.tr.v8f16(ptr addrspace(1) [[INPTR:%.*]])
// CHECK-GFX1200-NEXT: ret <8 x half> [[TMP0]]
//
v8h test_amdgcn_global_load_tr_v8f16(global v8h* inptr)
v8h test_amdgcn_global_load_tr_b128_v8f16(global v8h* inptr)
{
return __builtin_amdgcn_global_load_tr_v8f16(inptr);
return __builtin_amdgcn_global_load_tr_b128_v8f16(inptr);
}

// CHECK-GFX1200-LABEL: @test_amdgcn_global_load_tr_b128_v8bf16(
// CHECK-GFX1200-NEXT: entry:
// CHECK-GFX1200-NEXT: [[TMP0:%.*]] = tail call <8 x bfloat> @llvm.amdgcn.global.load.tr.v8bf16(ptr addrspace(1) [[INPTR:%.*]])
// CHECK-GFX1200-NEXT: ret <8 x bfloat> [[TMP0]]
//
v8bf16 test_amdgcn_global_load_tr_b128_v8bf16(global v8bf16* inptr)
{
return __builtin_amdgcn_global_load_tr_b128_v8bf16(inptr);
}
38 changes: 19 additions & 19 deletions clang/test/CodeGenOpenCL/builtins-amdgcn-global-load-tr-w64.cl
Original file line number Diff line number Diff line change
Expand Up @@ -4,44 +4,44 @@

typedef half v4h __attribute__((ext_vector_type(4)));
typedef short v4s __attribute__((ext_vector_type(4)));
typedef __bf16 v4bf16 __attribute__((ext_vector_type(4)));

// Wave64

//
// amdgcn_global_load_tr
//

// CHECK-GFX1200-LABEL: @test_amdgcn_global_load_tr_i32(
// CHECK-GFX1200-LABEL: @test_amdgcn_global_load_tr_b64_i32(
// CHECK-GFX1200-NEXT: entry:
// CHECK-GFX1200-NEXT: [[TMP0:%.*]] = tail call i32 @llvm.amdgcn.global.load.tr.i32(ptr addrspace(1) [[INPTR:%.*]])
// CHECK-GFX1200-NEXT: ret i32 [[TMP0]]
//
int test_amdgcn_global_load_tr_i32(global int* inptr)
int test_amdgcn_global_load_tr_b64_i32(global int* inptr)
{
return __builtin_amdgcn_global_load_tr_i32(inptr);
return __builtin_amdgcn_global_load_tr_b64_i32(inptr);
}

//
// amdgcn_global_load_tr
//

// CHECK-GFX1200-LABEL: @test_amdgcn_global_load_tr_v4i16(
// CHECK-GFX1200-LABEL: @test_amdgcn_global_load_tr_b128_v4i16(
// CHECK-GFX1200-NEXT: entry:
// CHECK-GFX1200-NEXT: [[TMP0:%.*]] = tail call <4 x i16> @llvm.amdgcn.global.load.tr.v4i16(ptr addrspace(1) [[INPTR:%.*]])
// CHECK-GFX1200-NEXT: ret <4 x i16> [[TMP0]]
//
v4s test_amdgcn_global_load_tr_v4i16(global v4s* inptr)
v4s test_amdgcn_global_load_tr_b128_v4i16(global v4s* inptr)
{
return __builtin_amdgcn_global_load_tr_v4i16(inptr);
return __builtin_amdgcn_global_load_tr_b128_v4i16(inptr);
}

// CHECK-GFX1200-LABEL: @test_amdgcn_global_load_tr_v4f16(
// CHECK-GFX1200-LABEL: @test_amdgcn_global_load_tr_b128_v4f16(
// CHECK-GFX1200-NEXT: entry:
// CHECK-GFX1200-NEXT: [[TMP0:%.*]] = tail call <4 x half> @llvm.amdgcn.global.load.tr.v4f16(ptr addrspace(1) [[INPTR:%.*]])
// CHECK-GFX1200-NEXT: ret <4 x half> [[TMP0]]
//
v4h test_amdgcn_global_load_tr_v4f16(global v4h* inptr)
v4h test_amdgcn_global_load_tr_b128_v4f16(global v4h* inptr)
{
return __builtin_amdgcn_global_load_tr_v4f16(inptr);
return __builtin_amdgcn_global_load_tr_b128_v4f16(inptr);
}

// CHECK-GFX1200-LABEL: @test_amdgcn_global_load_tr_b128_v4bf16(
// CHECK-GFX1200-NEXT: entry:
// CHECK-GFX1200-NEXT: [[TMP0:%.*]] = tail call <4 x bfloat> @llvm.amdgcn.global.load.tr.v4bf16(ptr addrspace(1) [[INPTR:%.*]])
// CHECK-GFX1200-NEXT: ret <4 x bfloat> [[TMP0]]
//
v4bf16 test_amdgcn_global_load_tr_b128_v4bf16(global v4bf16* inptr)
{
return __builtin_amdgcn_global_load_tr_b128_v4bf16(inptr);
}
2 changes: 2 additions & 0 deletions clang/test/SemaCXX/namespace-alias.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ namespace I {
namespace A1 { int i; }

namespace A2 = A1;

namespace A3::extra::specifiers = A2; // expected-error {{alias must be a single identifier}}
}

int f() {
Expand Down
2 changes: 1 addition & 1 deletion clang/www/c_dr_status.html
Original file line number Diff line number Diff line change
Expand Up @@ -1686,7 +1686,7 @@ <h2 id="cdr">C defect report implementation status</h2>
<td><a href="https://www.open-std.org/jtc1/sc22/wg14/www/docs/dr_290.htm">290</a></td>
<td>C99</td>
<td>FLT_EVAL_METHOD and extra precision and/or range</td>
<td class="unknown" align="center">Unknown</td>
<td class="none" align="center">No</td>
</tr>
<tr id="291">
<td><a href="https://www.open-std.org/jtc1/sc22/wg14/www/docs/dr_291.htm">291</a></td>
Expand Down
2 changes: 1 addition & 1 deletion clang/www/c_status.html
Original file line number Diff line number Diff line change
Expand Up @@ -471,7 +471,7 @@ <h2 id="c11">C11 implementation status</h2>
<tr>
<td>Constant expressions</td>
<td><a href="https://www.open-std.org/jtc1/sc22/wg14/www/docs/n1365.htm">N1365</a></td>
<td class="unknown" align="center">Unknown</td>
<td class="Clang 16" align="center">Full</td>
</tr>
<tr>
<td>Contractions and expression evaluation methods</td>
Expand Down
42 changes: 41 additions & 1 deletion clang/www/cxx_status.html
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,47 @@ <h2 id="cxx26">C++2c implementation status</h2>
<td><a href="https://wg21.link/P2864R2">P2864R2</a></td>
<td class="full" align="center">Clang 18</td>
</tr>

<!-- Winter 2024 papers (Tokyo) -->
<tr>
<td>Disallow Binding a Returned Glvalue to a Temporary</td>
<td><a href="https://wg21.link/P2748R5">P2748R5</a></td>
<td class="none" align="center">No</td>
</tr>
<tr>
<td>Clarifying rules for brace elision in aggregate initialization</td>
<td><a href="https://wg21.link/P3106R1">P3106R1</a> (<a href="#dr">DR</a>)</td>
<td class="none" align="center">No</td>
</tr>
<tr>
<td>Attributes for Structured Bindings</td>
<td><a href="https://wg21.link/P0609R3">P0609R3</a></td>
<td class="none" align="center">No</td>
</tr>
<tr>
<td>Module Declarations Shouldn’t be Macros</td>
<td><a href="https://wg21.link/P3034R1">P3034R1</a> (<a href="#dr">DR</a>)</td>
<td class="none" align="center">No</td>
</tr>
<tr>
<td>Trivial infinite loops are not Undefined Behavior</td>
<td><a href="https://wg21.link/P2809R3">P2809R3</a> (<a href="#dr">DR</a>)</td>
<td class="none" align="center">No</td>
</tr>
<tr>
<td>Erroneous behaviour for uninitialized reads</td>
<td><a href="https://wg21.link/P2795R5">P2795R5</a></td>
<td class="none" align="center">No</td>
</tr>
<tr>
<td><tt>= delete("should have a reason");</tt></td>
<td><a href="https://wg21.link/P2573R2">P2573R2</a></td>
<td class="none" align="center">No</td>
</tr>
<tr>
<td>Variadic friends</td>
<td><a href="https://wg21.link/P2893R3">P2893R3</a></td>
<td class="none" align="center">No</td>
</tr>
</table>
</details>

Expand Down
19 changes: 19 additions & 0 deletions cmake/Modules/GetDarwinLinkerVersion.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Get the linker version on Darwin
function(get_darwin_linker_version variable)
set(LINK_VERSION)
set(LD_V_OUTPUT)
execute_process(
COMMAND sh -c "${CMAKE_LINKER} -v 2>&1 | head -1"
RESULT_VARIABLE HAD_ERROR
OUTPUT_VARIABLE LD_V_OUTPUT
)
if (HAD_ERROR)
message(FATAL_ERROR "${CMAKE_LINKER} failed with status ${HAD_ERROR}")
endif()
if ("${LD_V_OUTPUT}" MATCHES ".*ld64-([0-9.]+).*")
string(REGEX REPLACE ".*ld64-([0-9.]+).*" "\\1" LINK_VERSION ${LD_V_OUTPUT})
elseif ("${LD_V_OUTPUT}" MATCHES "[^0-9]*([0-9.]+).*")
string(REGEX REPLACE "[^0-9]*([0-9.]+).*" "\\1" LINK_VERSION ${LD_V_OUTPUT})
endif()
set(${variable} ${LINK_VERSION} PARENT_SCOPE)
endfunction()
10 changes: 10 additions & 0 deletions compiler-rt/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ include(SetPlatformToolchainTools)
include(base-config-ix)
include(CompilerRTUtils)
include(CMakeDependentOption)
include(GetDarwinLinkerVersion)

option(COMPILER_RT_BUILD_BUILTINS "Build builtins" ON)
mark_as_advanced(COMPILER_RT_BUILD_BUILTINS)
Expand Down Expand Up @@ -444,6 +445,15 @@ else()
set(SANITIZER_USE_SYMBOLS FALSE)
endif()

# Get the linker version while configuring compiler-rt and explicitly pass it
# in cflags during testing. This fixes the compiler/linker version mismatch
# issue when running a clang built with a newer Xcode in an older Xcode
set(COMPILER_RT_DARWIN_LINKER_VERSION)
if (APPLE AND NOT CMAKE_LINKER MATCHES ".*lld.*")
get_darwin_linker_version(COMPILER_RT_DARWIN_LINKER_VERSION)
message(STATUS "Host linker version: ${COMPILER_RT_DARWIN_LINKER_VERSION}")
endif()

# Build sanitizer runtimes with debug info.
if(MSVC)
# Use /Z7 instead of /Zi for the asan runtime. This avoids the LNK4099
Expand Down
2 changes: 1 addition & 1 deletion compiler-rt/cmake/config-ix.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -461,7 +461,7 @@ if(APPLE)
set(ORC_SUPPORTED_OS osx)
endif()

set(DEFAULT_SANITIZER_MIN_OSX_VERSION 10.10)
set(DEFAULT_SANITIZER_MIN_OSX_VERSION 10.13)
set(DARWIN_osx_MIN_VER_FLAG "-mmacosx-version-min")
if(NOT SANITIZER_MIN_OSX_VERSION)
string(REGEX MATCH "${DARWIN_osx_MIN_VER_FLAG}=([.0-9]+)"
Expand Down
3 changes: 3 additions & 0 deletions compiler-rt/lib/asan/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,9 @@ SET(ASAN_HEADERS
include_directories(..)

set(ASAN_CFLAGS ${SANITIZER_COMMON_CFLAGS})

append_list_if(MSVC /Zl ASAN_CFLAGS)

set(ASAN_COMMON_DEFINITIONS ${COMPILER_RT_ASAN_SHADOW_SCALE_DEFINITION})

append_rtti_flag(OFF ASAN_CFLAGS)
Expand Down
14 changes: 8 additions & 6 deletions compiler-rt/lib/asan/tests/asan_noinst_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ TEST(AddressSanitizer, InternalSimpleDeathTest) {
EXPECT_DEATH(exit(1), "");
}

static void MallocStress(size_t n) {
static void *MallocStress(void *NumOfItrPtr) {
size_t n = *((size_t *)NumOfItrPtr);
u32 seed = my_rand();
BufferedStackTrace stack1;
stack1.trace_buffer[0] = 0xa123;
Expand Down Expand Up @@ -90,20 +91,21 @@ static void MallocStress(size_t n) {
}
for (size_t i = 0; i < vec.size(); i++)
__asan::asan_free(vec[i], &stack3, __asan::FROM_MALLOC);
return nullptr;
}


TEST(AddressSanitizer, NoInstMallocTest) {
MallocStress(ASAN_LOW_MEMORY ? 300000 : 1000000);
const size_t kNumIterations = (ASAN_LOW_MEMORY) ? 300000 : 1000000;
MallocStress((void *)&kNumIterations);
}

TEST(AddressSanitizer, ThreadedMallocStressTest) {
const int kNumThreads = 4;
const int kNumIterations = (ASAN_LOW_MEMORY) ? 10000 : 100000;
const size_t kNumIterations = (ASAN_LOW_MEMORY) ? 10000 : 100000;
pthread_t t[kNumThreads];
for (int i = 0; i < kNumThreads; i++) {
PTHREAD_CREATE(&t[i], 0, (void* (*)(void *x))MallocStress,
(void*)kNumIterations);
PTHREAD_CREATE(&t[i], 0, (void *(*)(void *x))MallocStress,
(void *)&kNumIterations);
}
for (int i = 0; i < kNumThreads; i++) {
PTHREAD_JOIN(t[i], 0);
Expand Down
2 changes: 2 additions & 0 deletions compiler-rt/lib/sanitizer_common/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,8 @@ include_directories(..)
set(SANITIZER_COMMON_DEFINITIONS
HAVE_RPC_XDR_H=${HAVE_RPC_XDR_H})

# note: L not I, this is nodefaultlibs for msvc
append_list_if(MSVC /Zl SANITIZER_COMMON_CFLAGS)
set(SANITIZER_CFLAGS ${SANITIZER_COMMON_CFLAGS})

# Too many existing bugs, needs cleanup.
Expand Down
6 changes: 3 additions & 3 deletions compiler-rt/lib/tsan/rtl/tsan_interceptors_posix.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2169,8 +2169,7 @@ static bool is_sync_signal(ThreadSignalContext *sctx, int sig,
return false;
#endif
return sig == SIGSEGV || sig == SIGBUS || sig == SIGILL || sig == SIGTRAP ||
sig == SIGABRT || sig == SIGFPE || sig == SIGPIPE || sig == SIGSYS ||
sig == SIGPROF;
sig == SIGABRT || sig == SIGFPE || sig == SIGPIPE || sig == SIGSYS;
}

void sighandler(int sig, __sanitizer_siginfo *info, void *ctx) {
Expand All @@ -2181,7 +2180,8 @@ void sighandler(int sig, __sanitizer_siginfo *info, void *ctx) {
return;
}
// Don't mess with synchronous signals.
const bool sync = is_sync_signal(sctx, sig, info);
const bool sync = is_sync_signal(sctx, sig, info) ||
(sig == SIGPROF && thr->is_inited && !thr->is_dead);
if (sync ||
// If we are in blocking function, we can safely process it now
// (but check if we are in a recursive interceptor,
Expand Down
10 changes: 5 additions & 5 deletions compiler-rt/lib/tsan/rtl/tsan_rtl_thread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,10 @@ void ThreadStart(ThreadState *thr, Tid tid, tid_t os_id,
}
Free(thr->tctx->sync);

#if !SANITIZER_GO
thr->is_inited = true;
#endif

uptr stk_addr = 0;
uptr stk_size = 0;
uptr tls_addr = 0;
Expand Down Expand Up @@ -200,15 +204,11 @@ void ThreadStart(ThreadState *thr, Tid tid, tid_t os_id,
}

void ThreadContext::OnStarted(void *arg) {
thr = static_cast<ThreadState *>(arg);
DPrintf("#%d: ThreadStart\n", tid);
new (thr) ThreadState(tid);
thr = new (arg) ThreadState(tid);
if (common_flags()->detect_deadlocks)
thr->dd_lt = ctx->dd->CreateLogicalThread(tid);
thr->tctx = this;
#if !SANITIZER_GO
thr->is_inited = true;
#endif
}

void ThreadFinish(ThreadState *thr) {
Expand Down
1 change: 1 addition & 0 deletions compiler-rt/lib/ubsan/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ set(UBSAN_HEADERS
include_directories(..)

set(UBSAN_CFLAGS ${SANITIZER_COMMON_CFLAGS})
append_list_if(MSVC /Zl UBSAN_CFLAGS)
append_rtti_flag(OFF UBSAN_CFLAGS)
append_list_if(SANITIZER_CAN_USE_CXXABI -DUBSAN_CAN_USE_CXXABI UBSAN_CFLAGS)

Expand Down
4 changes: 4 additions & 0 deletions compiler-rt/test/lit.common.cfg.py
Original file line number Diff line number Diff line change
Expand Up @@ -882,6 +882,10 @@ def is_windows_lto_supported():
elif config.use_lld and (not config.has_lld):
config.unsupported = True

if config.host_os == "Darwin":
if getattr(config, "darwin_linker_version", None):
extra_cflags += ["-mlinker-version=" + config.darwin_linker_version]

# Append any extra flags passed in lit_config
append_target_cflags = lit_config.params.get("append_target_cflags", None)
if append_target_cflags:
Expand Down
1 change: 1 addition & 0 deletions compiler-rt/test/lit.common.configured.in
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ set_default("expensive_checks", @LLVM_ENABLE_EXPENSIVE_CHECKS_PYBOOL@)
set_default("test_standalone_build_libs", @COMPILER_RT_TEST_STANDALONE_BUILD_LIBS_PYBOOL@)
set_default("has_compiler_rt_libatomic", @COMPILER_RT_BUILD_STANDALONE_LIBATOMIC_PYBOOL@)
set_default("aarch64_sme", @COMPILER_RT_HAS_AARCH64_SME_PYBOOL@)
set_default("darwin_linker_version", "@COMPILER_RT_DARWIN_LINKER_VERSION@")
# True iff the test suite supports ignoring the test compiler's runtime library path
# and using `config.compiler_rt_libdir` instead. This only matters when the runtime
# library paths differ.
Expand Down
248 changes: 248 additions & 0 deletions flang/include/flang/Optimizer/CodeGen/FIROpPatterns.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,248 @@
//===-- FIROpPatterns.h -- FIR operation conversion patterns ----*- 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 FORTRAN_OPTIMIZER_CODEGEN_FIROPPATTERNS_H
#define FORTRAN_OPTIMIZER_CODEGEN_FIROPPATTERNS_H

#include "flang/Optimizer/CodeGen/TypeConverter.h"
#include "mlir/Conversion/LLVMCommon/Pattern.h"
#include "mlir/Dialect/LLVMIR/LLVMDialect.h"

namespace fir {

struct FIRToLLVMPassOptions;

static constexpr unsigned defaultAddressSpace = 0u;

class ConvertFIRToLLVMPattern : public mlir::ConvertToLLVMPattern {
public:
ConvertFIRToLLVMPattern(llvm::StringRef rootOpName,
mlir::MLIRContext *context,
const fir::LLVMTypeConverter &typeConverter,
const fir::FIRToLLVMPassOptions &options,
mlir::PatternBenefit benefit = 1);

protected:
mlir::Type convertType(mlir::Type ty) const {
return lowerTy().convertType(ty);
}

// Convert FIR type to LLVM without turning fir.box<T> into memory
// reference.
mlir::Type convertObjectType(mlir::Type firType) const;

mlir::LLVM::ConstantOp
genI32Constant(mlir::Location loc, mlir::ConversionPatternRewriter &rewriter,
int value) const;

mlir::LLVM::ConstantOp
genConstantOffset(mlir::Location loc,
mlir::ConversionPatternRewriter &rewriter,
int offset) const;

/// Perform an extension or truncation as needed on an integer value. Lowering
/// to the specific target may involve some sign-extending or truncation of
/// values, particularly to fit them from abstract box types to the
/// appropriate reified structures.
mlir::Value integerCast(mlir::Location loc,
mlir::ConversionPatternRewriter &rewriter,
mlir::Type ty, mlir::Value val) const;
struct TypePair {
mlir::Type fir;
mlir::Type llvm;
};

TypePair getBoxTypePair(mlir::Type firBoxTy) const;

/// Construct code sequence to extract the specific value from a `fir.box`.
mlir::Value getValueFromBox(mlir::Location loc, TypePair boxTy,
mlir::Value box, mlir::Type resultTy,
mlir::ConversionPatternRewriter &rewriter,
int boxValue) const;

/// Method to construct code sequence to get the triple for dimension `dim`
/// from a box.
llvm::SmallVector<mlir::Value, 3>
getDimsFromBox(mlir::Location loc, llvm::ArrayRef<mlir::Type> retTys,
TypePair boxTy, mlir::Value box, mlir::Value dim,
mlir::ConversionPatternRewriter &rewriter) const;

llvm::SmallVector<mlir::Value, 3>
getDimsFromBox(mlir::Location loc, llvm::ArrayRef<mlir::Type> retTys,
TypePair boxTy, mlir::Value box, int dim,
mlir::ConversionPatternRewriter &rewriter) const;

mlir::Value
loadDimFieldFromBox(mlir::Location loc, TypePair boxTy, mlir::Value box,
mlir::Value dim, int off, mlir::Type ty,
mlir::ConversionPatternRewriter &rewriter) const;

mlir::Value
getDimFieldFromBox(mlir::Location loc, TypePair boxTy, mlir::Value box,
int dim, int off, mlir::Type ty,
mlir::ConversionPatternRewriter &rewriter) const;

mlir::Value getStrideFromBox(mlir::Location loc, TypePair boxTy,
mlir::Value box, unsigned dim,
mlir::ConversionPatternRewriter &rewriter) const;

/// Read base address from a fir.box. Returned address has type ty.
mlir::Value
getBaseAddrFromBox(mlir::Location loc, TypePair boxTy, mlir::Value box,
mlir::ConversionPatternRewriter &rewriter) const;

mlir::Value
getElementSizeFromBox(mlir::Location loc, mlir::Type resultTy, TypePair boxTy,
mlir::Value box,
mlir::ConversionPatternRewriter &rewriter) const;

// Get the element type given an LLVM type that is of the form
// (array|struct|vector)+ and the provided indexes.
mlir::Type getBoxEleTy(mlir::Type type,
llvm::ArrayRef<std::int64_t> indexes) const;

// Return LLVM type of the object described by a fir.box of \p boxType.
mlir::Type getLlvmObjectTypeFromBoxType(mlir::Type boxType) const;

/// Read the address of the type descriptor from a box.
mlir::Value
loadTypeDescAddress(mlir::Location loc, TypePair boxTy, mlir::Value box,
mlir::ConversionPatternRewriter &rewriter) const;

// Load the attribute from the \p box and perform a check against \p maskValue
// The final comparison is implemented as `(attribute & maskValue) != 0`.
mlir::Value genBoxAttributeCheck(mlir::Location loc, TypePair boxTy,
mlir::Value box,
mlir::ConversionPatternRewriter &rewriter,
unsigned maskValue) const;

template <typename... ARGS>
mlir::LLVM::GEPOp genGEP(mlir::Location loc, mlir::Type ty,
mlir::ConversionPatternRewriter &rewriter,
mlir::Value base, ARGS... args) const {
llvm::SmallVector<mlir::LLVM::GEPArg> cv = {args...};
auto llvmPtrTy =
mlir::LLVM::LLVMPointerType::get(ty.getContext(), /*addressSpace=*/0);
return rewriter.create<mlir::LLVM::GEPOp>(loc, llvmPtrTy, ty, base, cv);
}

// Find the Block in which the alloca should be inserted.
// The order to recursively find the proper block:
// 1. An OpenMP Op that will be outlined.
// 2. A LLVMFuncOp
// 3. The first ancestor that is an OpenMP Op or a LLVMFuncOp
mlir::Block *getBlockForAllocaInsert(mlir::Operation *op) const;

// Generate an alloca of size 1 for an object of type \p llvmObjectTy in the
// allocation address space provided for the architecture in the DataLayout
// specification. If the address space is different from the devices
// program address space we perform a cast. In the case of most architectures
// the program and allocation address space will be the default of 0 and no
// cast will be emitted.
mlir::Value
genAllocaAndAddrCastWithType(mlir::Location loc, mlir::Type llvmObjectTy,
unsigned alignment,
mlir::ConversionPatternRewriter &rewriter) const;

const fir::LLVMTypeConverter &lowerTy() const {
return *static_cast<const fir::LLVMTypeConverter *>(
this->getTypeConverter());
}

void attachTBAATag(mlir::LLVM::AliasAnalysisOpInterface op,
mlir::Type baseFIRType, mlir::Type accessFIRType,
mlir::LLVM::GEPOp gep) const {
lowerTy().attachTBAATag(op, baseFIRType, accessFIRType, gep);
}

unsigned
getAllocaAddressSpace(mlir::ConversionPatternRewriter &rewriter) const;

unsigned
getProgramAddressSpace(mlir::ConversionPatternRewriter &rewriter) const;

const fir::FIRToLLVMPassOptions &options;

using ConvertToLLVMPattern::match;
using ConvertToLLVMPattern::matchAndRewrite;
};

template <typename SourceOp>
class FIROpConversion : public ConvertFIRToLLVMPattern {
public:
using OpAdaptor = typename SourceOp::Adaptor;

explicit FIROpConversion(const LLVMTypeConverter &typeConverter,
const fir::FIRToLLVMPassOptions &options,
mlir::PatternBenefit benefit = 1)
: ConvertFIRToLLVMPattern(SourceOp::getOperationName(),
&typeConverter.getContext(), typeConverter,
options, benefit) {}

/// Wrappers around the RewritePattern methods that pass the derived op type.
void rewrite(mlir::Operation *op, mlir::ArrayRef<mlir::Value> operands,
mlir::ConversionPatternRewriter &rewriter) const final {
rewrite(mlir::cast<SourceOp>(op),
OpAdaptor(operands, mlir::cast<SourceOp>(op)), rewriter);
}
mlir::LogicalResult match(mlir::Operation *op) const final {
return match(mlir::cast<SourceOp>(op));
}
mlir::LogicalResult
matchAndRewrite(mlir::Operation *op, mlir::ArrayRef<mlir::Value> operands,
mlir::ConversionPatternRewriter &rewriter) const final {
return matchAndRewrite(mlir::cast<SourceOp>(op),
OpAdaptor(operands, mlir::cast<SourceOp>(op)),
rewriter);
}

/// Rewrite and Match methods that operate on the SourceOp type. These must be
/// overridden by the derived pattern class.
virtual mlir::LogicalResult match(SourceOp op) const {
llvm_unreachable("must override match or matchAndRewrite");
}
virtual void rewrite(SourceOp op, OpAdaptor adaptor,
mlir::ConversionPatternRewriter &rewriter) const {
llvm_unreachable("must override rewrite or matchAndRewrite");
}
virtual mlir::LogicalResult
matchAndRewrite(SourceOp op, OpAdaptor adaptor,
mlir::ConversionPatternRewriter &rewriter) const {
if (mlir::failed(match(op)))
return mlir::failure();
rewrite(op, adaptor, rewriter);
return mlir::success();
}

private:
using ConvertFIRToLLVMPattern::matchAndRewrite;
using ConvertToLLVMPattern::match;
};

/// FIR conversion pattern template
template <typename FromOp>
class FIROpAndTypeConversion : public FIROpConversion<FromOp> {
public:
using FIROpConversion<FromOp>::FIROpConversion;
using OpAdaptor = typename FromOp::Adaptor;

mlir::LogicalResult
matchAndRewrite(FromOp op, OpAdaptor adaptor,
mlir::ConversionPatternRewriter &rewriter) const final {
mlir::Type ty = this->convertType(op.getType());
return doRewrite(op, ty, adaptor, rewriter);
}

virtual mlir::LogicalResult
doRewrite(FromOp addr, mlir::Type ty, OpAdaptor adaptor,
mlir::ConversionPatternRewriter &rewriter) const = 0;
};

} // namespace fir

#endif // FORTRAN_OPTIMIZER_CODEGEN_FIROPPATTERNS_H
4 changes: 2 additions & 2 deletions flang/include/flang/Optimizer/CodeGen/TypeConverter.h
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,8 @@ class LLVMTypeConverter : public mlir::LLVMTypeConverter {
// to LLVM IR dialect here.
//
// fir.complex<T> | std.complex<T> --> llvm<"{t,t}">
template <typename C> mlir::Type convertComplexType(C cmplx) const {
LLVM_DEBUG(llvm::dbgs() << "type convert: " << cmplx << '\n');
template <typename C>
mlir::Type convertComplexType(C cmplx) const {
auto eleTy = cmplx.getElementType();
return convertType(specifics->complexMemoryType(eleTy));
}
Expand Down
1 change: 1 addition & 0 deletions flang/lib/Optimizer/CodeGen/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ add_flang_library(FIRCodeGen
CGOps.cpp
CodeGen.cpp
CodeGenOpenMP.cpp
FIROpPatterns.cpp
PreCGRewrite.cpp
TBAABuilder.cpp
Target.cpp
Expand Down
486 changes: 70 additions & 416 deletions flang/lib/Optimizer/CodeGen/CodeGen.cpp

Large diffs are not rendered by default.

315 changes: 315 additions & 0 deletions flang/lib/Optimizer/CodeGen/FIROpPatterns.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,315 @@
//===-- CodeGen.cpp -- bridge to lower to LLVM ----------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
//
// Coding style: https://mlir.llvm.org/getting_started/DeveloperGuide/
//
//===----------------------------------------------------------------------===//

#include "flang/Optimizer/CodeGen/FIROpPatterns.h"
#include "mlir/Dialect/OpenMP/OpenMPDialect.h"
#include "llvm/Support/Debug.h"

static inline mlir::Type getLlvmPtrType(mlir::MLIRContext *context,
unsigned addressSpace = 0) {
return mlir::LLVM::LLVMPointerType::get(context, addressSpace);
}

static unsigned getTypeDescFieldId(mlir::Type ty) {
auto isArray = fir::dyn_cast_ptrOrBoxEleTy(ty).isa<fir::SequenceType>();
return isArray ? kOptTypePtrPosInBox : kDimsPosInBox;
}

namespace fir {

ConvertFIRToLLVMPattern::ConvertFIRToLLVMPattern(
llvm::StringRef rootOpName, mlir::MLIRContext *context,
const fir::LLVMTypeConverter &typeConverter,
const fir::FIRToLLVMPassOptions &options, mlir::PatternBenefit benefit)
: ConvertToLLVMPattern(rootOpName, context, typeConverter, benefit),
options(options) {}

// Convert FIR type to LLVM without turning fir.box<T> into memory
// reference.
mlir::Type
ConvertFIRToLLVMPattern::convertObjectType(mlir::Type firType) const {
if (auto boxTy = firType.dyn_cast<fir::BaseBoxType>())
return lowerTy().convertBoxTypeAsStruct(boxTy);
return lowerTy().convertType(firType);
}

mlir::LLVM::ConstantOp ConvertFIRToLLVMPattern::genI32Constant(
mlir::Location loc, mlir::ConversionPatternRewriter &rewriter,
int value) const {
mlir::Type i32Ty = rewriter.getI32Type();
mlir::IntegerAttr attr = rewriter.getI32IntegerAttr(value);
return rewriter.create<mlir::LLVM::ConstantOp>(loc, i32Ty, attr);
}

mlir::LLVM::ConstantOp ConvertFIRToLLVMPattern::genConstantOffset(
mlir::Location loc, mlir::ConversionPatternRewriter &rewriter,
int offset) const {
mlir::Type ity = lowerTy().offsetType();
mlir::IntegerAttr cattr = rewriter.getI32IntegerAttr(offset);
return rewriter.create<mlir::LLVM::ConstantOp>(loc, ity, cattr);
}

/// Perform an extension or truncation as needed on an integer value. Lowering
/// to the specific target may involve some sign-extending or truncation of
/// values, particularly to fit them from abstract box types to the
/// appropriate reified structures.
mlir::Value
ConvertFIRToLLVMPattern::integerCast(mlir::Location loc,
mlir::ConversionPatternRewriter &rewriter,
mlir::Type ty, mlir::Value val) const {
auto valTy = val.getType();
// If the value was not yet lowered, lower its type so that it can
// be used in getPrimitiveTypeSizeInBits.
if (!valTy.isa<mlir::IntegerType>())
valTy = convertType(valTy);
auto toSize = mlir::LLVM::getPrimitiveTypeSizeInBits(ty);
auto fromSize = mlir::LLVM::getPrimitiveTypeSizeInBits(valTy);
if (toSize < fromSize)
return rewriter.create<mlir::LLVM::TruncOp>(loc, ty, val);
if (toSize > fromSize)
return rewriter.create<mlir::LLVM::SExtOp>(loc, ty, val);
return val;
}

fir::ConvertFIRToLLVMPattern::TypePair
ConvertFIRToLLVMPattern::getBoxTypePair(mlir::Type firBoxTy) const {
mlir::Type llvmBoxTy =
lowerTy().convertBoxTypeAsStruct(mlir::cast<fir::BaseBoxType>(firBoxTy));
return TypePair{firBoxTy, llvmBoxTy};
}

/// Construct code sequence to extract the specific value from a `fir.box`.
mlir::Value ConvertFIRToLLVMPattern::getValueFromBox(
mlir::Location loc, TypePair boxTy, mlir::Value box, mlir::Type resultTy,
mlir::ConversionPatternRewriter &rewriter, int boxValue) const {
if (box.getType().isa<mlir::LLVM::LLVMPointerType>()) {
auto pty = getLlvmPtrType(resultTy.getContext());
auto p = rewriter.create<mlir::LLVM::GEPOp>(
loc, pty, boxTy.llvm, box,
llvm::ArrayRef<mlir::LLVM::GEPArg>{0, boxValue});
auto loadOp = rewriter.create<mlir::LLVM::LoadOp>(loc, resultTy, p);
attachTBAATag(loadOp, boxTy.fir, nullptr, p);
return loadOp;
}
return rewriter.create<mlir::LLVM::ExtractValueOp>(loc, box, boxValue);
}

/// Method to construct code sequence to get the triple for dimension `dim`
/// from a box.
llvm::SmallVector<mlir::Value, 3> ConvertFIRToLLVMPattern::getDimsFromBox(
mlir::Location loc, llvm::ArrayRef<mlir::Type> retTys, TypePair boxTy,
mlir::Value box, mlir::Value dim,
mlir::ConversionPatternRewriter &rewriter) const {
mlir::Value l0 =
loadDimFieldFromBox(loc, boxTy, box, dim, 0, retTys[0], rewriter);
mlir::Value l1 =
loadDimFieldFromBox(loc, boxTy, box, dim, 1, retTys[1], rewriter);
mlir::Value l2 =
loadDimFieldFromBox(loc, boxTy, box, dim, 2, retTys[2], rewriter);
return {l0, l1, l2};
}

llvm::SmallVector<mlir::Value, 3> ConvertFIRToLLVMPattern::getDimsFromBox(
mlir::Location loc, llvm::ArrayRef<mlir::Type> retTys, TypePair boxTy,
mlir::Value box, int dim, mlir::ConversionPatternRewriter &rewriter) const {
mlir::Value l0 =
getDimFieldFromBox(loc, boxTy, box, dim, 0, retTys[0], rewriter);
mlir::Value l1 =
getDimFieldFromBox(loc, boxTy, box, dim, 1, retTys[1], rewriter);
mlir::Value l2 =
getDimFieldFromBox(loc, boxTy, box, dim, 2, retTys[2], rewriter);
return {l0, l1, l2};
}

mlir::Value ConvertFIRToLLVMPattern::loadDimFieldFromBox(
mlir::Location loc, TypePair boxTy, mlir::Value box, mlir::Value dim,
int off, mlir::Type ty, mlir::ConversionPatternRewriter &rewriter) const {
assert(box.getType().isa<mlir::LLVM::LLVMPointerType>() &&
"descriptor inquiry with runtime dim can only be done on descriptor "
"in memory");
mlir::LLVM::GEPOp p = genGEP(loc, boxTy.llvm, rewriter, box, 0,
static_cast<int>(kDimsPosInBox), dim, off);
auto loadOp = rewriter.create<mlir::LLVM::LoadOp>(loc, ty, p);
attachTBAATag(loadOp, boxTy.fir, nullptr, p);
return loadOp;
}

mlir::Value ConvertFIRToLLVMPattern::getDimFieldFromBox(
mlir::Location loc, TypePair boxTy, mlir::Value box, int dim, int off,
mlir::Type ty, mlir::ConversionPatternRewriter &rewriter) const {
if (box.getType().isa<mlir::LLVM::LLVMPointerType>()) {
mlir::LLVM::GEPOp p = genGEP(loc, boxTy.llvm, rewriter, box, 0,
static_cast<int>(kDimsPosInBox), dim, off);
auto loadOp = rewriter.create<mlir::LLVM::LoadOp>(loc, ty, p);
attachTBAATag(loadOp, boxTy.fir, nullptr, p);
return loadOp;
}
return rewriter.create<mlir::LLVM::ExtractValueOp>(
loc, box, llvm::ArrayRef<std::int64_t>{kDimsPosInBox, dim, off});
}

mlir::Value ConvertFIRToLLVMPattern::getStrideFromBox(
mlir::Location loc, TypePair boxTy, mlir::Value box, unsigned dim,
mlir::ConversionPatternRewriter &rewriter) const {
auto idxTy = lowerTy().indexType();
return getDimFieldFromBox(loc, boxTy, box, dim, kDimStridePos, idxTy,
rewriter);
}

/// Read base address from a fir.box. Returned address has type ty.
mlir::Value ConvertFIRToLLVMPattern::getBaseAddrFromBox(
mlir::Location loc, TypePair boxTy, mlir::Value box,
mlir::ConversionPatternRewriter &rewriter) const {
mlir::Type resultTy = ::getLlvmPtrType(boxTy.llvm.getContext());
return getValueFromBox(loc, boxTy, box, resultTy, rewriter, kAddrPosInBox);
}

mlir::Value ConvertFIRToLLVMPattern::getElementSizeFromBox(
mlir::Location loc, mlir::Type resultTy, TypePair boxTy, mlir::Value box,
mlir::ConversionPatternRewriter &rewriter) const {
return getValueFromBox(loc, boxTy, box, resultTy, rewriter, kElemLenPosInBox);
}

// Get the element type given an LLVM type that is of the form
// (array|struct|vector)+ and the provided indexes.
mlir::Type ConvertFIRToLLVMPattern::getBoxEleTy(
mlir::Type type, llvm::ArrayRef<std::int64_t> indexes) const {
for (unsigned i : indexes) {
if (auto t = type.dyn_cast<mlir::LLVM::LLVMStructType>()) {
assert(!t.isOpaque() && i < t.getBody().size());
type = t.getBody()[i];
} else if (auto t = type.dyn_cast<mlir::LLVM::LLVMArrayType>()) {
type = t.getElementType();
} else if (auto t = type.dyn_cast<mlir::VectorType>()) {
type = t.getElementType();
} else {
fir::emitFatalError(mlir::UnknownLoc::get(type.getContext()),
"request for invalid box element type");
}
}
return type;
}

// Return LLVM type of the object described by a fir.box of \p boxType.
mlir::Type ConvertFIRToLLVMPattern::getLlvmObjectTypeFromBoxType(
mlir::Type boxType) const {
mlir::Type objectType = fir::dyn_cast_ptrOrBoxEleTy(boxType);
assert(objectType && "boxType must be a box type");
return this->convertType(objectType);
}

/// Read the address of the type descriptor from a box.
mlir::Value ConvertFIRToLLVMPattern::loadTypeDescAddress(
mlir::Location loc, TypePair boxTy, mlir::Value box,
mlir::ConversionPatternRewriter &rewriter) const {
unsigned typeDescFieldId = getTypeDescFieldId(boxTy.fir);
mlir::Type tdescType = lowerTy().convertTypeDescType(rewriter.getContext());
return getValueFromBox(loc, boxTy, box, tdescType, rewriter, typeDescFieldId);
}

// Load the attribute from the \p box and perform a check against \p maskValue
// The final comparison is implemented as `(attribute & maskValue) != 0`.
mlir::Value ConvertFIRToLLVMPattern::genBoxAttributeCheck(
mlir::Location loc, TypePair boxTy, mlir::Value box,
mlir::ConversionPatternRewriter &rewriter, unsigned maskValue) const {
mlir::Type attrTy = rewriter.getI32Type();
mlir::Value attribute =
getValueFromBox(loc, boxTy, box, attrTy, rewriter, kAttributePosInBox);
mlir::LLVM::ConstantOp attrMask = genConstantOffset(loc, rewriter, maskValue);
auto maskRes =
rewriter.create<mlir::LLVM::AndOp>(loc, attrTy, attribute, attrMask);
mlir::LLVM::ConstantOp c0 = genConstantOffset(loc, rewriter, 0);
return rewriter.create<mlir::LLVM::ICmpOp>(loc, mlir::LLVM::ICmpPredicate::ne,
maskRes, c0);
}

// Find the Block in which the alloca should be inserted.
// The order to recursively find the proper block:
// 1. An OpenMP Op that will be outlined.
// 2. A LLVMFuncOp
// 3. The first ancestor that is an OpenMP Op or a LLVMFuncOp
mlir::Block *
ConvertFIRToLLVMPattern::getBlockForAllocaInsert(mlir::Operation *op) const {
if (auto iface = mlir::dyn_cast<mlir::omp::OutlineableOpenMPOpInterface>(op))
return iface.getAllocaBlock();
if (auto llvmFuncOp = mlir::dyn_cast<mlir::LLVM::LLVMFuncOp>(op))
return &llvmFuncOp.front();
return getBlockForAllocaInsert(op->getParentOp());
}

// Generate an alloca of size 1 for an object of type \p llvmObjectTy in the
// allocation address space provided for the architecture in the DataLayout
// specification. If the address space is different from the devices
// program address space we perform a cast. In the case of most architectures
// the program and allocation address space will be the default of 0 and no
// cast will be emitted.
mlir::Value ConvertFIRToLLVMPattern::genAllocaAndAddrCastWithType(
mlir::Location loc, mlir::Type llvmObjectTy, unsigned alignment,
mlir::ConversionPatternRewriter &rewriter) const {
auto thisPt = rewriter.saveInsertionPoint();
mlir::Operation *parentOp = rewriter.getInsertionBlock()->getParentOp();
if (mlir::isa<mlir::omp::DeclareReductionOp>(parentOp)) {
// DeclareReductionOp has multiple child regions. We want to get the first
// block of whichever of those regions we are currently in
mlir::Region *parentRegion = rewriter.getInsertionBlock()->getParent();
rewriter.setInsertionPointToStart(&parentRegion->front());
} else {
mlir::Block *insertBlock = getBlockForAllocaInsert(parentOp);
rewriter.setInsertionPointToStart(insertBlock);
}
auto size = genI32Constant(loc, rewriter, 1);
unsigned allocaAs = getAllocaAddressSpace(rewriter);
unsigned programAs = getProgramAddressSpace(rewriter);

mlir::Value al = rewriter.create<mlir::LLVM::AllocaOp>(
loc, ::getLlvmPtrType(llvmObjectTy.getContext(), allocaAs), llvmObjectTy,
size, alignment);

// if our allocation address space, is not the same as the program address
// space, then we must emit a cast to the program address space before use.
// An example case would be on AMDGPU, where the allocation address space is
// the numeric value 5 (private), and the program address space is 0
// (generic).
if (allocaAs != programAs) {
al = rewriter.create<mlir::LLVM::AddrSpaceCastOp>(
loc, ::getLlvmPtrType(llvmObjectTy.getContext(), programAs), al);
}

rewriter.restoreInsertionPoint(thisPt);
return al;
}

unsigned ConvertFIRToLLVMPattern::getAllocaAddressSpace(
mlir::ConversionPatternRewriter &rewriter) const {
mlir::Operation *parentOp = rewriter.getInsertionBlock()->getParentOp();
assert(parentOp != nullptr &&
"expected insertion block to have parent operation");
if (auto module = parentOp->getParentOfType<mlir::ModuleOp>())
if (mlir::Attribute addrSpace =
mlir::DataLayout(module).getAllocaMemorySpace())
return llvm::cast<mlir::IntegerAttr>(addrSpace).getUInt();
return defaultAddressSpace;
}

unsigned ConvertFIRToLLVMPattern::getProgramAddressSpace(
mlir::ConversionPatternRewriter &rewriter) const {
mlir::Operation *parentOp = rewriter.getInsertionBlock()->getParentOp();
assert(parentOp != nullptr &&
"expected insertion block to have parent operation");
if (auto module = parentOp->getParentOfType<mlir::ModuleOp>())
if (mlir::Attribute addrSpace =
mlir::DataLayout(module).getProgramMemorySpace())
return llvm::cast<mlir::IntegerAttr>(addrSpace).getUInt();
return defaultAddressSpace;
}

} // namespace fir
2 changes: 2 additions & 0 deletions libc/config/linux/x86_64/entrypoints.txt
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,9 @@ set(TARGET_LIBC_ENTRYPOINTS
libc.src.stdlib.qsort_r
libc.src.stdlib.rand
libc.src.stdlib.srand
libc.src.stdlib.strfromd
libc.src.stdlib.strfromf
libc.src.stdlib.strfroml
libc.src.stdlib.strtod
libc.src.stdlib.strtof
libc.src.stdlib.strtol
Expand Down
2 changes: 2 additions & 0 deletions libc/spec/stdc.td
Original file line number Diff line number Diff line change
Expand Up @@ -962,6 +962,8 @@ def StdC : StandardSpec<"stdc"> {
FunctionSpec<"srand", RetValSpec<VoidType>, [ArgSpec<UnsignedIntType>]>,

FunctionSpec<"strfromf", RetValSpec<IntType>, [ArgSpec<CharRestrictedPtr>, ArgSpec<SizeTType>, ArgSpec<ConstCharRestrictedPtr>, ArgSpec<FloatType>]>,
FunctionSpec<"strfromd", RetValSpec<IntType>, [ArgSpec<CharRestrictedPtr>, ArgSpec<SizeTType>, ArgSpec<ConstCharRestrictedPtr>, ArgSpec<DoubleType>]>,
FunctionSpec<"strfroml", RetValSpec<IntType>, [ArgSpec<CharRestrictedPtr>, ArgSpec<SizeTType>, ArgSpec<ConstCharRestrictedPtr>, ArgSpec<LongDoubleType>]>,

FunctionSpec<"strtof", RetValSpec<FloatType>, [ArgSpec<ConstCharRestrictedPtr>, ArgSpec<CharRestrictedPtrPtr>]>,
FunctionSpec<"strtod", RetValSpec<DoubleType>, [ArgSpec<ConstCharRestrictedPtr>, ArgSpec<CharRestrictedPtrPtr>]>,
Expand Down
2 changes: 1 addition & 1 deletion libc/src/math/docs/add_math_function.md
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ implementation (which is very often glibc).

- Build and Run a specific unit test:
```
$ ninja libc.test.src.math.<func>_test
$ ninja libc.test.src.math.<func>_test.__unit__
$ projects/libc/test/src/math/libc.test.src.math.<func>_test
```

Expand Down
20 changes: 20 additions & 0 deletions libc/src/stdlib/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,26 @@ add_entrypoint_object(
.str_from_util
)

add_entrypoint_object(
strfromd
SRCS
strfromd.cpp
HDRS
strfromd.h
DEPENDS
.str_from_util
)

add_entrypoint_object(
strfroml
SRCS
strfroml.cpp
HDRS
strfroml.h
DEPENDS
.str_from_util
)

add_header_library(
str_from_util
HDRS
Expand Down
42 changes: 42 additions & 0 deletions libc/src/stdlib/strfromd.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
//===-- Implementation of strfromd ------------------------------*- 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
//
//===----------------------------------------------------------------------===//

#include "src/stdlib/strfromd.h"
#include "src/stdlib/str_from_util.h"

#include <stdarg.h>
#include <stddef.h>

namespace LIBC_NAMESPACE {

LLVM_LIBC_FUNCTION(int, strfromd,
(char *__restrict s, size_t n, const char *__restrict format,
double fp)) {
LIBC_ASSERT(s != nullptr);

printf_core::FormatSection section =
internal::parse_format_string(format, fp);
printf_core::WriteBuffer wb(s, (n > 0 ? n - 1 : 0));
printf_core::Writer writer(&wb);

int result = 0;
if (section.has_conv)
result = internal::strfromfloat_convert<double>(&writer, section);
else
result = writer.write(section.raw_string);

if (result < 0)
return result;

if (n > 0)
wb.buff[wb.buff_cur] = '\0';

return writer.get_chars_written();
}

} // namespace LIBC_NAMESPACE
21 changes: 21 additions & 0 deletions libc/src/stdlib/strfromd.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
//===-- Implementation header for strfromd ------------------------*- 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_LIBC_SRC_STDLIB_STRFROMD_H
#define LLVM_LIBC_SRC_STDLIB_STRFROMD_H

#include <stddef.h>

namespace LIBC_NAMESPACE {

int strfromd(char *__restrict s, size_t n, const char *__restrict format,
double fp);

} // namespace LIBC_NAMESPACE

#endif // LLVM_LIBC_SRC_STDLIB_STRFROMD_H
2 changes: 1 addition & 1 deletion libc/src/stdlib/strfromf.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,4 @@ int strfromf(char *__restrict s, size_t n, const char *__restrict format,

} // namespace LIBC_NAMESPACE

#endif // LLVM_LIBC_SRC_STDLIB_STRTOF_H
#endif // LLVM_LIBC_SRC_STDLIB_STRFROMF_H
47 changes: 47 additions & 0 deletions libc/src/stdlib/strfroml.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
//===-- Implementation of strfroml ------------------------------*- 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
//
//===----------------------------------------------------------------------===//

#include "src/stdlib/strfroml.h"
#include "src/stdlib/str_from_util.h"

#include <stdarg.h>
#include <stddef.h>

namespace LIBC_NAMESPACE {

LLVM_LIBC_FUNCTION(int, strfroml,
(char *__restrict s, size_t n, const char *__restrict format,
long double fp)) {
LIBC_ASSERT(s != nullptr);

printf_core::FormatSection section =
internal::parse_format_string(format, fp);

// To ensure that the conversion function actually uses long double,
// the length modifier has to be set to LenghtModifier::L
section.length_modifier = printf_core::LengthModifier::L;

printf_core::WriteBuffer wb(s, (n > 0 ? n - 1 : 0));
printf_core::Writer writer(&wb);

int result = 0;
if (section.has_conv)
result = internal::strfromfloat_convert<long double>(&writer, section);
else
result = writer.write(section.raw_string);

if (result < 0)
return result;

if (n > 0)
wb.buff[wb.buff_cur] = '\0';

return writer.get_chars_written();
}

} // namespace LIBC_NAMESPACE
21 changes: 21 additions & 0 deletions libc/src/stdlib/strfroml.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
//===-- Implementation header for strfroml ------------------------*- 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_LIBC_SRC_STDLIB_STRFROML_H
#define LLVM_LIBC_SRC_STDLIB_STRFROML_H

#include <stddef.h>

namespace LIBC_NAMESPACE {

int strfroml(char *__restrict s, size_t n, const char *__restrict format,
long double fp);

} // namespace LIBC_NAMESPACE

#endif // LLVM_LIBC_SRC_STDLIB_STRFROML_H
31 changes: 31 additions & 0 deletions libc/test/src/stdlib/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -168,16 +168,47 @@ add_libc_test(
.strtol_test_support
)

add_header_library(
strfrom_test_support
HDRS
StrfromTest.h
DEPENDS
libc.src.__support.CPP.type_traits
)

add_libc_test(
strfromf_test
SUITE
libc-stdlib-tests
SRCS
strfromf_test.cpp
DEPENDS
.strfrom_test_support
libc.src.stdlib.strfromf
)

add_libc_test(
strfromd_test
SUITE
libc-stdlib-tests
SRCS
strfromd_test.cpp
DEPENDS
.strfrom_test_support
libc.src.stdlib.strfromd
)

add_libc_test(
strfroml_test
SUITE
libc-stdlib-tests
SRCS
strfroml_test.cpp
DEPENDS
.strfrom_test_support
libc.src.stdlib.strfroml
)

add_libc_test(
abs_test
SUITE
Expand Down
Loading