mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-16 10:43:29 +00:00
compiler: handle nested unsigned shift being untyped after type resolution (#5497)
* apply compiler patch for fix of #5496 * add compiler/testdata smoketest * make diff error more explicit on difference * apply golden fix
This commit is contained in:
@@ -422,19 +422,19 @@ func (c *compilerContext) makeLLVMType(goType types.Type) llvm.Type {
|
|||||||
return c.ctx.Int8Type()
|
return c.ctx.Int8Type()
|
||||||
case types.Int16, types.Uint16:
|
case types.Int16, types.Uint16:
|
||||||
return c.ctx.Int16Type()
|
return c.ctx.Int16Type()
|
||||||
case types.Int32, types.Uint32:
|
case types.Int32, types.Uint32, types.UntypedRune:
|
||||||
return c.ctx.Int32Type()
|
return c.ctx.Int32Type()
|
||||||
case types.Int, types.Uint:
|
case types.Int, types.Uint, types.UntypedInt:
|
||||||
return c.intType
|
return c.intType
|
||||||
case types.Int64, types.Uint64:
|
case types.Int64, types.Uint64:
|
||||||
return c.ctx.Int64Type()
|
return c.ctx.Int64Type()
|
||||||
case types.Float32:
|
case types.Float32:
|
||||||
return c.ctx.FloatType()
|
return c.ctx.FloatType()
|
||||||
case types.Float64:
|
case types.Float64, types.UntypedFloat:
|
||||||
return c.ctx.DoubleType()
|
return c.ctx.DoubleType()
|
||||||
case types.Complex64:
|
case types.Complex64:
|
||||||
return c.ctx.StructType([]llvm.Type{c.ctx.FloatType(), c.ctx.FloatType()}, false)
|
return c.ctx.StructType([]llvm.Type{c.ctx.FloatType(), c.ctx.FloatType()}, false)
|
||||||
case types.Complex128:
|
case types.Complex128, types.UntypedComplex:
|
||||||
return c.ctx.StructType([]llvm.Type{c.ctx.DoubleType(), c.ctx.DoubleType()}, false)
|
return c.ctx.StructType([]llvm.Type{c.ctx.DoubleType(), c.ctx.DoubleType()}, false)
|
||||||
case types.String, types.UntypedString:
|
case types.String, types.UntypedString:
|
||||||
return c.getLLVMRuntimeType("_string")
|
return c.getLLVMRuntimeType("_string")
|
||||||
|
|||||||
+50
-24
@@ -123,53 +123,79 @@ func TestCompiler(t *testing.T) {
|
|||||||
t.Fatal("failed to read golden file:", err)
|
t.Fatal("failed to read golden file:", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !fuzzyEqualIR(mod.String(), string(expected)) {
|
if diff := diffIR(string(expected), mod.String()); diff != "" {
|
||||||
t.Errorf("output does not match expected output:\n%s", mod.String())
|
t.Errorf("output does not match expected output (re-run with -update to regenerate):\n%s", diff)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// fuzzyEqualIR returns true if the two LLVM IR strings passed in are roughly
|
// normalizeIR canonicalizes LLVM IR so a single golden file keeps matching
|
||||||
// equal. That means, only relevant lines are compared (excluding comments
|
// across LLVM versions. Golden files are written against LLVM <21; newer LLVM
|
||||||
// etc.).
|
// prints some attributes differently.
|
||||||
func fuzzyEqualIR(s1, s2 string) bool {
|
func normalizeIR(s string) string {
|
||||||
// Golden files are written using the pre-LLVM21 'nocapture' spelling,
|
// Golden files are written using the pre-LLVM21 'nocapture' spelling,
|
||||||
// which LLVM printed before any co-occurring attribute such as
|
// which LLVM printed before any co-occurring attribute such as
|
||||||
// 'readonly' (e.g. "ptr nocapture readonly"). LLVM 21+ prints the
|
// 'readonly' (e.g. "ptr nocapture readonly"). LLVM 21+ prints the
|
||||||
// equivalent 'captures(none)' instead, and after such attributes (e.g.
|
// equivalent 'captures(none)' instead, and after such attributes (e.g.
|
||||||
// "ptr readonly captures(none)"). Normalize both name and position back
|
// "ptr readonly captures(none)"). Normalize both name and position back
|
||||||
// to the old spelling to keep a single golden file working across LLVM
|
// to the old spelling.
|
||||||
// versions.
|
s = normalizeCapturesAttr(s)
|
||||||
s1 = normalizeCapturesAttr(s1)
|
|
||||||
s2 = normalizeCapturesAttr(s2)
|
|
||||||
|
|
||||||
// LLVM 21+ also added an explicit 'nocreateundeforpoison' attribute to
|
// LLVM 21+ also added an explicit 'nocreateundeforpoison' attribute to
|
||||||
// certain intrinsic declarations (e.g. llvm.umin) that were implicitly
|
// certain intrinsic declarations (e.g. llvm.umin) that were implicitly
|
||||||
// assumed not to create undef/poison before. It's unrelated to the
|
// assumed not to create undef/poison before. It's unrelated to the
|
||||||
// behavior under test, so ignore it for comparison.
|
// behavior under test, so ignore it for comparison.
|
||||||
s1 = strings.ReplaceAll(s1, "nocreateundeforpoison ", "")
|
s = strings.ReplaceAll(s, "nocreateundeforpoison ", "")
|
||||||
s2 = strings.ReplaceAll(s2, "nocreateundeforpoison ", "")
|
|
||||||
|
|
||||||
// LLVM 22 dropped the (redundant) i64 size argument from
|
// LLVM 22 dropped the (redundant) i64 size argument from
|
||||||
// llvm.lifetime.start/end. Normalize away that argument so golden files
|
// llvm.lifetime.start/end. Normalize away that argument so golden files
|
||||||
// written against the two-argument form still match.
|
// written against the two-argument form still match.
|
||||||
s1 = lifetimeSizeArgRe.ReplaceAllString(s1, "$1")
|
s = lifetimeSizeArgRe.ReplaceAllString(s, "$1")
|
||||||
s2 = lifetimeSizeArgRe.ReplaceAllString(s2, "$1")
|
|
||||||
|
|
||||||
lines1 := filterIrrelevantIRLines(strings.Split(s1, "\n"))
|
return s
|
||||||
lines2 := filterIrrelevantIRLines(strings.Split(s2, "\n"))
|
}
|
||||||
if len(lines1) != len(lines2) {
|
|
||||||
return false
|
// diffIR compares two LLVM IR strings, ignoring irrelevant lines (comments,
|
||||||
|
// empty lines, etc.) and normalizing LLVM-version-specific spellings via
|
||||||
|
// normalizeIR. It returns "" when they are equal. Otherwise it returns a
|
||||||
|
// compact diff of only the region that differs: the common prefix and suffix
|
||||||
|
// are trimmed, then the differing expected lines (prefixed "-") are shown
|
||||||
|
// followed by the differing actual lines (prefixed "+").
|
||||||
|
func diffIR(expected, actual string) string {
|
||||||
|
exp := filterIrrelevantIRLines(strings.Split(normalizeIR(expected), "\n"))
|
||||||
|
act := filterIrrelevantIRLines(strings.Split(normalizeIR(actual), "\n"))
|
||||||
|
|
||||||
|
// Trim the common prefix.
|
||||||
|
start := 0
|
||||||
|
for start < len(exp) && start < len(act) && exp[start] == act[start] {
|
||||||
|
start++
|
||||||
}
|
}
|
||||||
for i, line1 := range lines1 {
|
// Trim the common suffix.
|
||||||
line2 := lines2[i]
|
e, a := len(exp), len(act)
|
||||||
if line1 != line2 {
|
for e > start && a > start && exp[e-1] == act[a-1] {
|
||||||
return false
|
e--
|
||||||
}
|
a--
|
||||||
|
}
|
||||||
|
if start == e && start == a {
|
||||||
|
return "" // equal
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
var b strings.Builder
|
||||||
|
b.WriteString("first difference at relevant line ")
|
||||||
|
b.WriteString(strconv.Itoa(start + 1))
|
||||||
|
b.WriteString(":\n")
|
||||||
|
for _, line := range exp[start:e] {
|
||||||
|
b.WriteString("- ")
|
||||||
|
b.WriteString(line)
|
||||||
|
b.WriteByte('\n')
|
||||||
|
}
|
||||||
|
for _, line := range act[start:a] {
|
||||||
|
b.WriteString("+ ")
|
||||||
|
b.WriteString(line)
|
||||||
|
b.WriteByte('\n')
|
||||||
|
}
|
||||||
|
return b.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
// capturesNoneAttrRe matches a co-occurring attribute directly followed by
|
// capturesNoneAttrRe matches a co-occurring attribute directly followed by
|
||||||
|
|||||||
Vendored
+5
@@ -66,6 +66,11 @@ func complexSub(x, y complex64) complex64 {
|
|||||||
return x - y
|
return x - y
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func shiftNested(x uint64) uint64 {
|
||||||
|
k := 3
|
||||||
|
return x >> (1 << k) // https://github.com/tinygo-org/tinygo/issues/5496
|
||||||
|
}
|
||||||
|
|
||||||
func complexMul(x, y complex64) complex64 {
|
func complexMul(x, y complex64) complex64 {
|
||||||
return x * y
|
return x * y
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
+7
@@ -176,6 +176,13 @@ entry:
|
|||||||
ret { float, float } %3
|
ret { float, float } %3
|
||||||
}
|
}
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden i64 @main.shiftNested(i64 %x, ptr %context) unnamed_addr #1 {
|
||||||
|
entry:
|
||||||
|
%0 = lshr i64 %x, 8
|
||||||
|
ret i64 %0
|
||||||
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden { float, float } @main.complexMul(float %x.r, float %x.i, float %y.r, float %y.i, ptr %context) unnamed_addr #1 {
|
define hidden { float, float } @main.complexMul(float %x.r, float %x.i, float %y.r, float %y.i, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
|
|||||||
Reference in New Issue
Block a user