mirror of
https://github.com/LLVMParty/llvm-nanobind
synced 2026-06-21 13:43:38 +00:00
Fix more LLVM-C test bugs
This commit is contained in:
+17
-14
@@ -4,14 +4,14 @@ Last updated: February 19, 2026
|
||||
|
||||
This document tracks issues relevant to llvm-c-test parity work.
|
||||
|
||||
## 1. Poison Constant ValueKind Mismatch
|
||||
## 1. Poison/Undef Predicate Ordering in Echo (Historical)
|
||||
|
||||
- Status: Reproduced in both C and Python implementations.
|
||||
- Status: Fixed in both vendored C llvm-c-test and Python implementation.
|
||||
- Scope: `--echo`
|
||||
- Symptom: `LLVMGetValueKind` reports poison as the wrong kind during cloning.
|
||||
- Error text: `LLVMGetValueKind returned incorrect type`
|
||||
- Lit coverage: `llvm-c/llvm-c-test/inputs/echo_poison_known_bug.ll`
|
||||
- Notes: This currently appears to be an upstream LLVM-C API behavior issue.
|
||||
- Root cause: `LLVMIsUndef(poison)` is true, so checking `undef` before
|
||||
`poison` misclassifies poison constants.
|
||||
- Fix summary: check poison before undef in constant cloning.
|
||||
- Lit coverage: `llvm-c/llvm-c-test/inputs/poison.ll`
|
||||
|
||||
## 2. BitCast Echo Crash (Historical)
|
||||
|
||||
@@ -35,14 +35,17 @@ This document tracks issues relevant to llvm-c-test parity work.
|
||||
- Scope: `--echo`
|
||||
- Lit coverage: `llvm-c/llvm-c-test/inputs/types_extended.ll`
|
||||
|
||||
## 5. Object Symbol Section Name Crash in C llvm-c-test
|
||||
## 5. Object Symbol Size Crash in C llvm-c-test
|
||||
|
||||
- Status: Fixed in vendored `llvm-c-test` code.
|
||||
- Scope: `--object-list-symbols`
|
||||
- Root cause: C test code called `LLVMGetSectionName` after
|
||||
`LLVMMoveToContainingSection` without checking section-iterator end.
|
||||
- Fix: Guard with `LLVMObjectFileIsSectionIteratorAtEnd` and print `"(null)"`
|
||||
when no containing section exists.
|
||||
- Root cause: C test code called `LLVMGetSymbolSize` for symbols without a
|
||||
containing section (for example file symbols), which can assert in debug
|
||||
builds.
|
||||
- Fix:
|
||||
- Guard section iterator end after `LLVMMoveToContainingSection`.
|
||||
- For symbols with no containing section, print section as `"(null)"` and
|
||||
size as `0` without calling `LLVMGetSymbolSize`.
|
||||
- Files:
|
||||
- Fix: `llvm-c/llvm-c-test/object.c`
|
||||
- Coverage: `llvm-c/llvm-c-test/inputs/object_symbols.test`
|
||||
@@ -50,8 +53,8 @@ This document tracks issues relevant to llvm-c-test parity work.
|
||||
## Upstreaming Plan
|
||||
|
||||
1. Upstream llvm-c-test fixes that are API-call correctness issues
|
||||
(`object.c` section-iterator end guard).
|
||||
(`object.c` section-iterator and symbol-size guard).
|
||||
2. Keep regression lit tests for previously failing behaviors
|
||||
(`echo_constant_fp.ll`, `echo_bitcast.ll`, `object_symbols.test`).
|
||||
3. For unresolved upstream API behavior (`echo_poison_known_bug.ll`), keep a
|
||||
negative test and reference it in upstream discussion.
|
||||
3. Keep regression lit tests for historical failures and ensure they pass on
|
||||
both C and Python implementations (`poison.ll`).
|
||||
|
||||
+29
-12
@@ -359,18 +359,18 @@ static LLVMValueRef clone_constant_impl(LLVMValueRef Cst, LLVMModuleRef M) {
|
||||
return LLVMConstNull(Ty);
|
||||
}
|
||||
|
||||
// Try undef
|
||||
if (LLVMIsUndef(Cst)) {
|
||||
check_value_kind(Cst, LLVMUndefValueValueKind);
|
||||
return LLVMGetUndef(TypeCloner(M).Clone(Cst));
|
||||
}
|
||||
|
||||
// Try poison
|
||||
if (LLVMIsPoison(Cst)) {
|
||||
check_value_kind(Cst, LLVMPoisonValueValueKind);
|
||||
return LLVMGetPoison(TypeCloner(M).Clone(Cst));
|
||||
}
|
||||
|
||||
// Try undef
|
||||
if (LLVMIsUndef(Cst)) {
|
||||
check_value_kind(Cst, LLVMUndefValueValueKind);
|
||||
return LLVMGetUndef(TypeCloner(M).Clone(Cst));
|
||||
}
|
||||
|
||||
// Try null
|
||||
if (LLVMIsNull(Cst)) {
|
||||
check_value_kind(Cst, LLVMConstantTokenNoneValueKind);
|
||||
@@ -566,15 +566,29 @@ struct FunCloner {
|
||||
if (i != VMap.end())
|
||||
return i->second;
|
||||
|
||||
if (LLVMGetNumOperands(Src) != 1)
|
||||
report_fatal_error("BlockAddress should have exactly one operand");
|
||||
unsigned NumOps = LLVMGetNumOperands(Src);
|
||||
if (NumOps != 1 && NumOps != 2)
|
||||
report_fatal_error("BlockAddress should have one or two operands");
|
||||
|
||||
LLVMValueRef BBVal = LLVMGetOperand(Src, 0);
|
||||
unsigned BBOpIndex = NumOps - 1;
|
||||
LLVMValueRef BBVal = LLVMGetOperand(Src, BBOpIndex);
|
||||
if (!LLVMValueIsBasicBlock(BBVal))
|
||||
report_fatal_error("BlockAddress operand is not a basic block");
|
||||
|
||||
LLVMBasicBlockRef SrcBB = LLVMValueAsBasicBlock(BBVal);
|
||||
LLVMValueRef SrcFn = LLVMGetBasicBlockParent(SrcBB);
|
||||
LLVMValueRef SrcFn = nullptr;
|
||||
if (NumOps == 2) {
|
||||
LLVMValueRef FnOp = LLVMGetOperand(Src, 0);
|
||||
if (!LLVMIsAFunction(FnOp))
|
||||
report_fatal_error("BlockAddress function operand is not a function");
|
||||
SrcFn = FnOp;
|
||||
} else {
|
||||
SrcFn = LLVMGetBasicBlockParent(SrcBB);
|
||||
}
|
||||
|
||||
if (LLVMGetBasicBlockParent(SrcBB) != SrcFn)
|
||||
report_fatal_error("BlockAddress function/block mismatch");
|
||||
|
||||
size_t FnNameLen;
|
||||
const char *FnName = LLVMGetValueName2(SrcFn, &FnNameLen);
|
||||
LLVMValueRef DstFn = LLVMGetNamedFunction(M, FnName);
|
||||
@@ -592,10 +606,13 @@ struct FunCloner {
|
||||
|
||||
// Try to clone everything in the llvm::Value hierarchy.
|
||||
LLVMValueRef CloneValue(LLVMValueRef Src) {
|
||||
// Handle BlockAddress by value kind first. Some LLVM revisions may not
|
||||
// classify BlockAddress as LLVMIsAConstant().
|
||||
if (LLVMGetValueKind(Src) == LLVMBlockAddressValueKind)
|
||||
return CloneBlockAddressConstant(Src);
|
||||
|
||||
// First, the value may be constant.
|
||||
if (LLVMIsAConstant(Src)) {
|
||||
if (LLVMGetValueKind(Src) == LLVMBlockAddressValueKind)
|
||||
return CloneBlockAddressConstant(Src);
|
||||
return clone_constant(Src, M);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
; RUN: set +o pipefail; llvm-as < %s | llvm-c-test --echo 2>&1 | FileCheck %s
|
||||
;
|
||||
; Known upstream LLVM-C limitation:
|
||||
; LLVMGetValueKind reports poison constants as UndefValue, causing echo to fail
|
||||
; when it validates ValueKind before cloning.
|
||||
|
||||
define i32 @test_poison() {
|
||||
entry:
|
||||
ret i32 poison
|
||||
}
|
||||
|
||||
; CHECK: LLVMGetValueKind returned incorrect type
|
||||
@@ -0,0 +1,11 @@
|
||||
; RUN: llvm-as < %s | llvm-dis > %t.orig
|
||||
; RUN: llvm-as < %s | llvm-c-test --echo > %t.echo
|
||||
; RUN: diff -w %t.orig %t.echo
|
||||
;
|
||||
; Regression: poison constants are also reported by LLVMIsUndef(), so echo must
|
||||
; check poison before undef when cloning constants.
|
||||
|
||||
define i32 @test_poison() {
|
||||
entry:
|
||||
ret i32 poison
|
||||
}
|
||||
@@ -23,7 +23,7 @@ define %NamedStruct @test_named_struct(%NamedStruct %s) {
|
||||
%1 = extractvalue %NamedStruct %s, 0
|
||||
%2 = extractvalue %NamedStruct %s, 1
|
||||
%3 = extractvalue %NamedStruct %s, 2
|
||||
%4 = insertvalue %NamedStruct undef, i32 %1, 0
|
||||
%4 = insertvalue %NamedStruct %s, i32 %1, 0
|
||||
%5 = insertvalue %NamedStruct %4, i64 %2, 1
|
||||
%6 = insertvalue %NamedStruct %5, ptr %3, 2
|
||||
ret %NamedStruct %6
|
||||
|
||||
@@ -79,12 +79,15 @@ int llvm_object_list_symbols(void) {
|
||||
sym = LLVMObjectFileCopySymbolIterator(O);
|
||||
while (sect && sym && !LLVMObjectFileIsSymbolIteratorAtEnd(O, sym)) {
|
||||
const char *SectionName = NULL;
|
||||
uint64_t SymbolSize = 0;
|
||||
|
||||
LLVMMoveToContainingSection(sect, sym);
|
||||
if (!LLVMObjectFileIsSectionIteratorAtEnd(O, sect))
|
||||
if (!LLVMObjectFileIsSectionIteratorAtEnd(O, sect)) {
|
||||
SectionName = LLVMGetSectionName(sect);
|
||||
SymbolSize = LLVMGetSymbolSize(sym);
|
||||
}
|
||||
printf("%s @0x%08" PRIx64 " +%" PRIu64 " (%s)\n", LLVMGetSymbolName(sym),
|
||||
LLVMGetSymbolAddress(sym), LLVMGetSymbolSize(sym),
|
||||
LLVMGetSymbolAddress(sym), SymbolSize,
|
||||
SectionName ? SectionName : "(null)");
|
||||
|
||||
LLVMMoveToNextSymbol(sym);
|
||||
|
||||
+29
-11
@@ -212,16 +212,16 @@ def clone_constant_impl(cst: llvm.Value, m: llvm.Module) -> llvm.Value:
|
||||
ty = TypeCloner(m).clone(cst)
|
||||
return ty.null()
|
||||
|
||||
# Try undef
|
||||
if cst.is_undef:
|
||||
check_value_kind(cst, llvm.ValueKind.UndefValue)
|
||||
return TypeCloner(m).clone(cst).undef()
|
||||
|
||||
# Try poison
|
||||
if cst.is_poison:
|
||||
check_value_kind(cst, llvm.ValueKind.PoisonValue)
|
||||
return TypeCloner(m).clone(cst).poison()
|
||||
|
||||
# Try undef
|
||||
if cst.is_undef:
|
||||
check_value_kind(cst, llvm.ValueKind.UndefValue)
|
||||
return TypeCloner(m).clone(cst).undef()
|
||||
|
||||
# Try null
|
||||
if cst.is_null:
|
||||
check_value_kind(cst, llvm.ValueKind.ConstantTokenNone)
|
||||
@@ -447,19 +447,34 @@ class FunCloner:
|
||||
if src in self.vmap:
|
||||
return self.vmap[src]
|
||||
|
||||
if src.num_operands != 1:
|
||||
raise RuntimeError("BlockAddress should have exactly one operand")
|
||||
num_ops = src.num_operands
|
||||
if num_ops not in (1, 2):
|
||||
raise RuntimeError("BlockAddress should have one or two operands")
|
||||
|
||||
bb_val = src.get_operand(0)
|
||||
bb_op_index = num_ops - 1
|
||||
bb_val = src.get_operand(bb_op_index)
|
||||
if not bb_val.value_is_basic_block:
|
||||
raise RuntimeError("BlockAddress operand is not a basic block")
|
||||
|
||||
src_bb = bb_val.value_as_basic_block()
|
||||
src_fn = src_bb.function
|
||||
if num_ops == 2:
|
||||
fn_op = src.get_operand(0)
|
||||
if not fn_op.is_function:
|
||||
raise RuntimeError("BlockAddress function operand is not a function")
|
||||
src_fn = fn_op
|
||||
else:
|
||||
src_fn = src_bb.function
|
||||
|
||||
if src_bb.function != src_fn:
|
||||
raise RuntimeError("BlockAddress function/block mismatch")
|
||||
|
||||
dst_fn = self.module.get_function(src_fn.name)
|
||||
if not dst_fn:
|
||||
raise RuntimeError("Could not find function for block address")
|
||||
|
||||
if dst_fn != self.fun:
|
||||
raise RuntimeError("Cross-function blockaddress is not supported")
|
||||
|
||||
dst_bb = self.declare_bb(src_bb)
|
||||
dst = dst_fn.block_address(dst_bb)
|
||||
self.vmap[src] = dst
|
||||
@@ -467,10 +482,13 @@ class FunCloner:
|
||||
|
||||
def clone_value(self, src: llvm.Value) -> llvm.Value:
|
||||
"""Clone a value, handling constants, params, and instructions."""
|
||||
# Handle BlockAddress by value kind first. Some LLVM revisions may not
|
||||
# classify BlockAddress as a constant in the IsA helpers.
|
||||
if src.value_kind == llvm.ValueKind.BlockAddress:
|
||||
return self.clone_block_address_constant(src)
|
||||
|
||||
# First, the value may be constant
|
||||
if src.is_constant:
|
||||
if src.value_kind == llvm.ValueKind.BlockAddress:
|
||||
return self.clone_block_address_constant(src)
|
||||
return clone_constant(src, self.module)
|
||||
|
||||
# Function argument should always be in the map
|
||||
|
||||
@@ -63,9 +63,15 @@ def object_list_symbols() -> int:
|
||||
|
||||
sym_name = sym.name
|
||||
sym_address = sym.address
|
||||
sym_size = sym.size
|
||||
# Some symbols have no containing section; match C tool output.
|
||||
sect_name = "(null)" if sect.is_at_end() else sect.name
|
||||
if sect.is_at_end():
|
||||
sect_name = "(null)"
|
||||
# Avoid LLVMGetSymbolSize on symbols without a containing
|
||||
# section (e.g. file symbols), which can assert in debug builds.
|
||||
sym_size = 0
|
||||
else:
|
||||
sect_name = sect.name
|
||||
sym_size = sym.size
|
||||
|
||||
print(f"{sym_name} @0x{sym_address:08x} +{sym_size} ({sect_name})")
|
||||
|
||||
|
||||
@@ -8105,6 +8105,21 @@ struct LLVMSymbolIteratorWrapper : NoMoveCopy {
|
||||
uint64_t get_size() const {
|
||||
check_valid();
|
||||
check_not_at_end("SymbolIterator.size");
|
||||
|
||||
// Some symbol kinds (e.g. file symbols) have no containing section.
|
||||
// LLVMGetSymbolSize can assert on those in debug builds.
|
||||
LLVMSectionIteratorRef sect = LLVMObjectFileCopySectionIterator(m_binary_ref);
|
||||
if (!sect)
|
||||
return 0;
|
||||
|
||||
LLVMMoveToContainingSection(sect, m_ref);
|
||||
bool HasContainingSection =
|
||||
!LLVMObjectFileIsSectionIteratorAtEnd(m_binary_ref, sect);
|
||||
LLVMDisposeSectionIterator(sect);
|
||||
|
||||
if (!HasContainingSection)
|
||||
return 0;
|
||||
|
||||
return LLVMGetSymbolSize(m_ref);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user