-
Notifications
You must be signed in to change notification settings - Fork 994
Description
Summary
I discovered an issue with device.AsmFull where it incorrectly processes MapUpdate operations that occur after the AsmFull call, causing the function to return unexpected values from future map modifications instead of the values present at call time.
Problem Description
The issue is in compiler/inlineasm.go at line 62, where the code uses break to exit a switch statement but continues processing subsequent MapUpdate operations in the referrers loop:
case *ssa.Call:
if r.Common() == instr {
break // ❌ Only breaks from switch, continues for loop
}This means that map updates happening after the AsmFull call are still collected and can affect the result.
Reproduction
package main
import (
"device"
"machine"
"time"
)
func main() {
machine.Serial.Configure(machine.UARTConfig{BaudRate: 115200})
time.Sleep(1 * time.Second)
place := map[string]interface{}{
"input": uint32(42),
}
result := device.AsmFull("mov {}, {input}", place)
place["input"] = uint32(44) // This MapUpdate affects the result!
println("Expected: 42, Got:", uint32(result))
}Expected Result
Expected: 42, Got: 42
Actual Result (Before Fix)
Expected: 42, Got: 44
Root Cause Analysis
The original code appears to intend to stop processing referrers when encountering the AsmFull call itself, but uses break which only exits the switch statement, not the entire for loop. This allows subsequent MapUpdate operations to be processed incorrectly.
Impact
This bug can cause: Unexpected behavior where AsmFull returns "future" values instead of call-time values
Thank you for maintaining TinyGo! This is an amazing project and I'm happy to contribute to its improvement. 😊