constant folding 2

This commit is contained in:
Sky Johnson 2025-06-11 22:10:17 -05:00
parent cf9f3af3f1
commit 121774005b
2 changed files with 535 additions and 426 deletions

File diff suppressed because it is too large Load Diff

View File

@ -173,7 +173,7 @@ func (cs *CompilerState) AddUpvalue(index uint8, isLocal bool) int {
return upvalueCount
}
// Optimized constant pool management with deduplication
// Enhanced constant pool management with better deduplication
func (cs *CompilerState) AddConstant(value Value) int {
// Generate unique key for deduplication
key := cs.valueKey(value)
@ -191,7 +191,7 @@ func (cs *CompilerState) AddConstant(value Value) int {
return index
}
// Generate unique key for value deduplication
// Enhanced value key generation for better deduplication
func (cs *CompilerState) valueKey(value Value) string {
switch value.Type {
case ValueNil:
@ -202,15 +202,41 @@ func (cs *CompilerState) valueKey(value Value) string {
}
return "bool:false"
case ValueNumber:
return fmt.Sprintf("number:%g", value.Data.(float64))
num := value.Data.(float64)
// Handle special numeric values
if num == 0 {
return "number:0"
} else if num == 1 {
return "number:1"
} else if num == -1 {
return "number:-1"
}
return fmt.Sprintf("number:%g", num)
case ValueString:
return fmt.Sprintf("string:%s", value.Data.(string))
str := value.Data.(string)
if str == "" {
return "string:empty"
}
// For very long strings, just use a hash to avoid memory issues
if len(str) > 100 {
return fmt.Sprintf("string:hash:%d", cs.simpleHash(str))
}
return fmt.Sprintf("string:%s", str)
default:
// For complex types, use memory address as fallback
return fmt.Sprintf("%T:%p", value.Data, value.Data)
}
}
// Simple hash function for long strings
func (cs *CompilerState) simpleHash(s string) uint32 {
var hash uint32
for _, c := range s {
hash = hash*31 + uint32(c)
}
return hash
}
// Optimized bytecode emission methods
func (cs *CompilerState) EmitByte(byte uint8) {
cs.Chunk.Code = append(cs.Chunk.Code, byte)
@ -240,7 +266,7 @@ func (cs *CompilerState) PatchJump(offset int) {
jump := len(cs.Chunk.Code) - offset - 2
if jump > 65535 {
// Jump distance too large - would need to implement long jumps
// Jump distance too large - could implement long jumps here
return
}
@ -352,7 +378,7 @@ func (cs *CompilerState) EmitStoreLocal(slot int) {
}
}
// Instruction pattern detection for optimization
// Enhanced instruction pattern detection for optimization
func (cs *CompilerState) GetLastInstruction() (Opcode, []uint16) {
if len(cs.Chunk.Code) == 0 {
return OpNoop, nil
@ -367,9 +393,11 @@ func (cs *CompilerState) GetLastInstruction() (Opcode, []uint16) {
// This is a complete instruction
operands := make([]uint16, operandCount)
for j := 0; j < operandCount; j++ {
if i+1+j*2 < len(cs.Chunk.Code) && i+2+j*2 < len(cs.Chunk.Code) {
operands[j] = uint16(cs.Chunk.Code[i+1+j*2]) |
(uint16(cs.Chunk.Code[i+2+j*2]) << 8)
}
}
return op, operands
}
@ -402,50 +430,6 @@ func (cs *CompilerState) ReplaceLastInstruction(op Opcode, operands ...uint16) b
return true
}
// Constant folding support
func (cs *CompilerState) TryConstantFolding(op Opcode, operands ...Value) *Value {
if len(operands) < 2 {
return nil
}
left, right := operands[0], operands[1]
// Only fold numeric operations for now
if left.Type != ValueNumber || right.Type != ValueNumber {
return nil
}
l := left.Data.(float64)
r := right.Data.(float64)
switch op {
case OpAdd:
return &Value{Type: ValueNumber, Data: l + r}
case OpSub:
return &Value{Type: ValueNumber, Data: l - r}
case OpMul:
return &Value{Type: ValueNumber, Data: l * r}
case OpDiv:
if r != 0 {
return &Value{Type: ValueNumber, Data: l / r}
}
case OpEq:
return &Value{Type: ValueBool, Data: l == r}
case OpNeq:
return &Value{Type: ValueBool, Data: l != r}
case OpLt:
return &Value{Type: ValueBool, Data: l < r}
case OpLte:
return &Value{Type: ValueBool, Data: l <= r}
case OpGt:
return &Value{Type: ValueBool, Data: l > r}
case OpGte:
return &Value{Type: ValueBool, Data: l >= r}
}
return nil
}
// Dead code elimination support
func (cs *CompilerState) MarkUnreachable(start, end int) {
if start >= 0 && end <= len(cs.Chunk.Code) {
@ -461,12 +445,14 @@ type OptimizationStats struct {
InstructionsOpt int
DeadCodeEliminated int
JumpsOptimized int
ConstantsDeduped int
}
func (cs *CompilerState) GetOptimizationStats() OptimizationStats {
// Count specialized instructions used
specialized := 0
noops := 0
constantsDeduped := len(cs.Constants) - len(cs.Chunk.Constants)
for i := 0; i < len(cs.Chunk.Code); {
op, _, next := DecodeInstruction(cs.Chunk.Code, i)
@ -482,6 +468,7 @@ func (cs *CompilerState) GetOptimizationStats() OptimizationStats {
return OptimizationStats{
InstructionsOpt: specialized,
DeadCodeEliminated: noops,
ConstantsDeduped: constantsDeduped,
}
}
@ -489,13 +476,31 @@ func (cs *CompilerState) SetLine(line int) {
cs.CurrentLine = line
}
// Debugging support
// Enhanced debugging support
func (cs *CompilerState) PrintChunk(name string) {
fmt.Printf("== %s ==\n", name)
fmt.Printf("Constants: %d\n", len(cs.Chunk.Constants))
fmt.Printf("Functions: %d\n", len(cs.Chunk.Functions))
fmt.Printf("Structs: %d\n", len(cs.Chunk.Structs))
fmt.Printf("Code size: %d bytes\n", len(cs.Chunk.Code))
stats := cs.GetOptimizationStats()
fmt.Printf("Optimizations: %d specialized, %d dead eliminated, %d constants deduped\n",
stats.InstructionsOpt, stats.DeadCodeEliminated, stats.ConstantsDeduped)
fmt.Println()
for offset := 0; offset < len(cs.Chunk.Code); {
offset = cs.disassembleInstruction(offset)
}
if len(cs.Chunk.Constants) > 0 {
fmt.Println("\nConstants:")
for i, constant := range cs.Chunk.Constants {
fmt.Printf("%4d: ", i)
cs.printValue(constant)
fmt.Println()
}
}
}
func (cs *CompilerState) disassembleInstruction(offset int) int {
@ -528,12 +533,14 @@ func (cs *CompilerState) disassembleInstruction(offset int) int {
switch op {
case OpLoadConst:
return cs.constantInstruction(offset)
case OpLoadLocal, OpStoreLocal:
case OpLoadLocal, OpStoreLocal, OpAddConst, OpSubConst, OpInc, OpDec:
return cs.byteInstruction(offset)
case OpJump, OpJumpIfTrue, OpJumpIfFalse:
return cs.jumpInstruction(offset, 1)
case OpLoopBack:
return cs.jumpInstruction(offset, -1)
case OpGetLocalField, OpSetLocalField, OpTestAndJump:
return cs.doubleByteInstruction(offset)
default:
fmt.Println()
return offset + 1
@ -570,6 +577,18 @@ func (cs *CompilerState) byteInstruction(offset int) int {
return offset + 3
}
func (cs *CompilerState) doubleByteInstruction(offset int) int {
if offset+4 >= len(cs.Chunk.Code) {
fmt.Println(" [incomplete]")
return offset + 1
}
arg1 := uint16(cs.Chunk.Code[offset+1]) | (uint16(cs.Chunk.Code[offset+2]) << 8)
arg2 := uint16(cs.Chunk.Code[offset+3]) | (uint16(cs.Chunk.Code[offset+4]) << 8)
fmt.Printf(" %4d %4d\n", arg1, arg2)
return offset + 5
}
func (cs *CompilerState) jumpInstruction(offset int, sign int) int {
if offset+2 >= len(cs.Chunk.Code) {
fmt.Println(" [incomplete]")
@ -593,9 +612,14 @@ func (cs *CompilerState) printValue(value Value) {
fmt.Print("false")
}
case ValueNumber:
fmt.Printf("%.2g", value.Data.(float64))
fmt.Printf("%.6g", value.Data.(float64))
case ValueString:
fmt.Printf("\"%s\"", value.Data.(string))
str := value.Data.(string)
if len(str) > 50 {
fmt.Printf("\"%s...\"", str[:47])
} else {
fmt.Printf("\"%s\"", str)
}
default:
fmt.Printf("<%s>", cs.valueTypeString(value.Type))
}