From d880413b53cba07ce613d9299e6d9ab48f037a84 Mon Sep 17 00:00:00 2001 From: palumbon Date: Mon, 20 Jul 2026 16:06:38 -0300 Subject: [PATCH 01/10] Using DROpalCompiler on tests --- Druid-Opal/DRBytecodeGeneratorTest.class.st | 24 ++------------------- Druid-Opal/DROpalCompiler.class.st | 6 ++---- 2 files changed, 4 insertions(+), 26 deletions(-) diff --git a/Druid-Opal/DRBytecodeGeneratorTest.class.st b/Druid-Opal/DRBytecodeGeneratorTest.class.st index 9f3f4fb9..5d592af2 100644 --- a/Druid-Opal/DRBytecodeGeneratorTest.class.st +++ b/Druid-Opal/DRBytecodeGeneratorTest.class.st @@ -143,32 +143,12 @@ DRBytecodeGeneratorTest >> generateMethodForSelector: aSelector [ { #category : 'utilities' } DRBytecodeGeneratorTest >> generateMethodFromCFG: aDRControlFlowGraph withSelector: selector [ - ^ self + ^ DROpalCompiler generateMethodFromCFG: aDRControlFlowGraph withSelector: selector numArgs: compilerCompiler irGenerator numberOfArguments ] -{ #category : 'utilities' } -DRBytecodeGeneratorTest >> generateMethodFromCFG: aDRControlFlowGraph withSelector: selector numArgs: numArgs [ - - aDRControlFlowGraph applyOptimisation: DRFrameInfoCleaner new. - aDRControlFlowGraph applyOptimisation: DRBranchCollapse new. - - aDRControlFlowGraph applyOptimisation: DRPhiSimplication new. - aDRControlFlowGraph applyOptimisation: (DRSCCP then: DRDeadCodeElimination). - - DRUnconditionalBackJumpScheluder new applyTo: aDRControlFlowGraph. - DRLocalVariableInstructionScheluder new applyTo: aDRControlFlowGraph. - aDRControlFlowGraph applyOptimisation: DRCleanScopes new. - aDRControlFlowGraph applyOptimisation: DRDeadCodeElimination new. - - bytecodeGenerator numArgs: numArgs. - bytecodeGenerator methodName: selector. - bytecodeGenerator generateTargetASTFromIR: aDRControlFlowGraph methodName: selector. - ^ bytecodeGenerator targetAST -] - { #category : 'running' } DRBytecodeGeneratorTest >> setUp [ super setUp. @@ -199,7 +179,7 @@ DRBytecodeGeneratorTest >> testBasicControlFlow [ cfg initialBasicBlock jumpTo: b. b return: (b add: 3 to: 4) ]. cfg scope: DRScope new. - method := self generateMethodFromCFG: cfg withSelector: #m numArgs: 0. + method := DROpalCompiler generateMethodFromCFG: cfg withSelector: #m numArgs: 0. result := method valueWithReceiver: nil arguments: { }. self assert: result equals: 7 diff --git a/Druid-Opal/DROpalCompiler.class.st b/Druid-Opal/DROpalCompiler.class.st index f7d887d7..2428a5ed 100644 --- a/Druid-Opal/DROpalCompiler.class.st +++ b/Druid-Opal/DROpalCompiler.class.st @@ -1,9 +1,6 @@ Class { #name : 'DROpalCompiler', #superclass : 'Object', - #classInstVars : [ - 'bytecodeGenerator' - ], #category : 'Druid-Opal-Objects', #package : 'Druid-Opal', #tag : 'Objects' @@ -20,6 +17,7 @@ DROpalCompiler class >> copyMethod: selector from: aClass to: newClass [ { #category : 'utilities' } DROpalCompiler class >> generateMethodFromCFG: aDRControlFlowGraph withSelector: selector numArgs: numArgs [ + | bytecodeGenerator | aDRControlFlowGraph applyOptimisation: DRFrameInfoCleaner new. aDRControlFlowGraph applyOptimisation: DRBranchCollapse new. @@ -31,6 +29,7 @@ DROpalCompiler class >> generateMethodFromCFG: aDRControlFlowGraph withSelector: aDRControlFlowGraph applyOptimisation: DRCleanScopes new. aDRControlFlowGraph applyOptimisation: DRDeadCodeElimination new. + bytecodeGenerator := DRBytecodeGenerator new. bytecodeGenerator numArgs: numArgs. bytecodeGenerator methodName: selector. bytecodeGenerator generateTargetASTFromIR: aDRControlFlowGraph methodName: selector. @@ -49,7 +48,6 @@ DROpalCompiler class >> recompile: selector from: oldClass to: newClass [ | compilerCompiler oldMethod cfg newMethod typeSystem newSelector | compilerCompiler := DRMethodCompilerCompiler new. typeSystem := DRCustomisationTypeSystem new. - bytecodeGenerator := DRBytecodeGenerator new. oldMethod := oldClass >> selector. compilerCompiler configureForCompilerClass: nil. From 766720db679dd0a05facecdb4be6991998049490 Mon Sep 17 00:00:00 2001 From: palumbon Date: Wed, 22 Jul 2026 12:37:58 -0300 Subject: [PATCH 02/10] Fix block dependencies + tests --- Druid-Opal/DRBytecodeGeneratorTest.class.st | 6 ++++ ...LocalVariableInstructionScheluder.class.st | 5 +++ ...lVariableInstructionScheluderTest.class.st | 31 +++++++++++++++++++ Druid-Opal/DrOpalExamples.class.st | 9 ++++++ Druid/DRAbstractInstruction.class.st | 2 +- Druid/DRBlockClosure.class.st | 2 +- Druid/DRIRGenerator.class.st | 3 +- Druid/DRStackInstruction.class.st | 10 ------ 8 files changed, 54 insertions(+), 14 deletions(-) diff --git a/Druid-Opal/DRBytecodeGeneratorTest.class.st b/Druid-Opal/DRBytecodeGeneratorTest.class.st index 5d592af2..99aaf732 100644 --- a/Druid-Opal/DRBytecodeGeneratorTest.class.st +++ b/Druid-Opal/DRBytecodeGeneratorTest.class.st @@ -203,6 +203,12 @@ DRBytecodeGeneratorTest >> testMethodEmpty [ self assertCompilationFor: #exampleEmptyMethod fromClass: OCOpalExamples ] +{ #category : 'tests' } +DRBytecodeGeneratorTest >> testMethodEmptyIf [ + + self assertCompilationFor: #emptyIf fromClass: DrOpalExamples +] + { #category : 'tests' } DRBytecodeGeneratorTest >> testMethodInline [ diff --git a/Druid-Opal/DRLocalVariableInstructionScheluder.class.st b/Druid-Opal/DRLocalVariableInstructionScheluder.class.st index 1f896371..1540a88f 100644 --- a/Druid-Opal/DRLocalVariableInstructionScheluder.class.st +++ b/Druid-Opal/DRLocalVariableInstructionScheluder.class.st @@ -49,6 +49,8 @@ DRLocalVariableInstructionScheluder >> shouldIgnore: aDRInstruction [ DRLocalVariableInstructionScheluder >> storePhiOperands: aDRPhiFunction [ | variableName loadInst cfg| + aDRPhiFunction hasUsers ifFalse: [ ^ self ]. + variableName := aDRPhiFunction result name asLowercase asDRValue. "add the new created temporary variable to the scope" @@ -64,11 +66,14 @@ DRLocalVariableInstructionScheluder >> storePhiOperands: aDRPhiFunction [ (aDRPhiFunction operandsWithoutMe allSatisfy: [ :op | op result isNoResult ]) ifTrue: [ "If they are already temporaries then it is ok" ^ self ]. + + aDRPhiFunction operands do: [ :op | op isNullValue ifTrue: [ "1halt." ^self ] ]. aDRPhiFunction operandsWithoutMe select: [ :op | op isInstruction ] thenDo: [ :op | | storeInst | + "op isPhiFunction ifTrue: [1halt. ^ self ]." storeInst := DRStoreTemporaryVariable operands: { variableName. diff --git a/Druid-Opal/DRLocalVariableInstructionScheluderTest.class.st b/Druid-Opal/DRLocalVariableInstructionScheluderTest.class.st index 1467fff3..83f086d3 100644 --- a/Druid-Opal/DRLocalVariableInstructionScheluderTest.class.st +++ b/Druid-Opal/DRLocalVariableInstructionScheluderTest.class.st @@ -12,6 +12,36 @@ DRLocalVariableInstructionScheluderTest >> newCompiler [ ^ DRMethodCompilerCompiler new ] +{ #category : 'tests' } +DRLocalVariableInstructionScheluderTest >> testOnLoops [ + + | cfg variableName phi b5 | + + cfg := self setUpCFGWithLoop. + b5 := cfg b1 predecessors first. + phi := cfg phiFunctions unique. + cfg b1 endInstruction replaceOperandAtIndex: 1 by: phi. + + variableName := 'ssa_', phi result id asString. + DRLocalVariableInstructionScheluder new applyTo: cfg. + + "Replace the Phi by a Load for the condition" + self assert: cfg b1 instructions first isLoadTemporaryVariable. + self assert: cfg b1 instructions first operand1 value equals: variableName. + + "Store the initial value" + self assert: b5 instructions second isStoreTemporaryVariable. + self assert: b5 instructions second operand1 value equals: variableName. + self assert: b5 instructions second operand2 equals: phi operand1. + + "Store inside the loop" + self assert: cfg b2 endInstruction isBackJump. + self assert: cfg b2 instructions second isStoreTemporaryVariable. + self assert: cfg b2 instructions second operand1 value equals: variableName. + self assert: cfg b2 instructions second operand2 equals: phi operand2. + +] + { #category : 'tests' } DRLocalVariableInstructionScheluderTest >> testOneUseDoNothing [ @@ -33,6 +63,7 @@ DRLocalVariableInstructionScheluderTest >> testPhiBecomeLoadTemp [ | cfg variableName | cfg := self setUpCFGWithConditionalWithPhi. + cfg b4 return: cfg phi. DRLocalVariableInstructionScheluder new applyTo: cfg. diff --git a/Druid-Opal/DrOpalExamples.class.st b/Druid-Opal/DrOpalExamples.class.st index 79aaba44..cd35e305 100644 --- a/Druid-Opal/DrOpalExamples.class.st +++ b/Druid-Opal/DrOpalExamples.class.st @@ -115,6 +115,15 @@ DrOpalExamples >> doubleOf: n [ DrOpalExamples >> empty [ ] +{ #category : 'as yet unclassified' } +DrOpalExamples >> emptyIf [ + | irow | + irow = nil + ifTrue: [ ] + ifFalse: [ ] + +] + { #category : 'as yet unclassified' } DrOpalExamples >> methodBlockConstant [ "a constant block closure" diff --git a/Druid/DRAbstractInstruction.class.st b/Druid/DRAbstractInstruction.class.st index 45f7ab46..e64e1d20 100644 --- a/Druid/DRAbstractInstruction.class.st +++ b/Druid/DRAbstractInstruction.class.st @@ -248,7 +248,7 @@ DRAbstractInstruction >> validateDependenciesAreUsedByMyself [ self dependencies do: [ :op | (op hasUser: self) ifFalse: [ - DRError signal: + Error signal: 'Dependency-user mismatch between: ' , self asString , ' and: ' , op asString ] ] ] diff --git a/Druid/DRBlockClosure.class.st b/Druid/DRBlockClosure.class.st index 7dae461d..7c81a55f 100644 --- a/Druid/DRBlockClosure.class.st +++ b/Druid/DRBlockClosure.class.st @@ -14,7 +14,7 @@ Class { { #category : 'comparing' } DRBlockClosure >> = otherCode [ - self class = otherCode class ifTrue: [ ^ self blockNode = otherCode blockNode ]. + self class = otherCode class ifTrue: [ ^ self blockNode == otherCode blockNode ]. ^ false ] diff --git a/Druid/DRIRGenerator.class.st b/Druid/DRIRGenerator.class.st index f013bb75..366739a7 100644 --- a/Druid/DRIRGenerator.class.st +++ b/Druid/DRIRGenerator.class.st @@ -1944,8 +1944,7 @@ DRIRGenerator >> resolveMessageSend: aRBMessageNode receiver: receiver [ | arguments | arguments := aRBMessageNode arguments collect: [ :e | - e acceptVisitor: self. - self popOperand ]. + self visitOperand: e ]. ^ self resolveMessageSend: aRBMessageNode diff --git a/Druid/DRStackInstruction.class.st b/Druid/DRStackInstruction.class.st index e821dcd3..2be3b6ce 100644 --- a/Druid/DRStackInstruction.class.st +++ b/Druid/DRStackInstruction.class.st @@ -129,13 +129,3 @@ DRStackInstruction >> stackSlotsDefiningAtDepth: anInteger [ s stackSlotsDefiningAtDepth: anInteger + self stackDelta ]. ^ definingSlots ] - -{ #category : 'validation' } -DRStackInstruction >> validateDependenciesAreUsedByMyself [ - - self dataDependencies do: [ :op | - (op hasUser: self) ifFalse: [ - DRError signal: - 'Dependency-user mismatch between: ' , self asString , ' and: ' - , op asString ] ] -] From 352c688e8ba00c70ff0f3252cb0f606fa4e8caf4 Mon Sep 17 00:00:00 2001 From: palumbon Date: Wed, 22 Jul 2026 12:43:06 -0300 Subject: [PATCH 03/10] Save assignments as temporaries inside the IR generator (important for inlines + merge states) --- Druid-Opal/DRBytecodeGeneratorTest.class.st | 6 ++++++ Druid-Opal/DRInstructionsTest.class.st | 4 ++-- Druid-Opal/DRMethodIRGenerator.class.st | 2 +- Druid-Opal/DrOpalExamples.class.st | 24 +++++++++++++++++++++ 4 files changed, 33 insertions(+), 3 deletions(-) diff --git a/Druid-Opal/DRBytecodeGeneratorTest.class.st b/Druid-Opal/DRBytecodeGeneratorTest.class.st index 99aaf732..ceee09cb 100644 --- a/Druid-Opal/DRBytecodeGeneratorTest.class.st +++ b/Druid-Opal/DRBytecodeGeneratorTest.class.st @@ -238,6 +238,12 @@ DRBytecodeGeneratorTest >> testMethodInlineFailsWithUnresolvedNonLocalReturns [ self assert: (err messageText beginsWith: 'Non-local return') ] ] +{ #category : 'tests' } +DRBytecodeGeneratorTest >> testMethodInlineLoop [ + + self assertCompilationFor: #methodWithoutInnerDependencies fromClass: DrOpalExamples +] + { #category : 'tests' } DRBytecodeGeneratorTest >> testMethodInlineObjectAccessInstVar [ diff --git a/Druid-Opal/DRInstructionsTest.class.st b/Druid-Opal/DRInstructionsTest.class.st index 59b40b34..7ac5ee2b 100644 --- a/Druid-Opal/DRInstructionsTest.class.st +++ b/Druid-Opal/DRInstructionsTest.class.st @@ -62,8 +62,8 @@ DRInstructionsTest >> testReplaceMessageSendUpdateInnerDependencies [ | cfg instruction message newInstruction | cfg := self generateDruidIRFor: #simpleMethodWithBlockReadingOuterVariable. cfg applyOptimisation: DRCleanControlFlow new. - instruction := cfg firstBasicBlock instructions fourth. - message := cfg firstBasicBlock instructions fifth. + instruction := cfg firstBasicBlock instructions third. + message := cfg firstBasicBlock instructions fourth. self assert: (instruction users includes: message). diff --git a/Druid-Opal/DRMethodIRGenerator.class.st b/Druid-Opal/DRMethodIRGenerator.class.st index e3a705ce..25f07299 100644 --- a/Druid-Opal/DRMethodIRGenerator.class.st +++ b/Druid-Opal/DRMethodIRGenerator.class.st @@ -27,7 +27,7 @@ DRMethodIRGenerator >> addAssignmentInstructionFrom: anAssignmentNode instructio store scope: scope. load scope: scope ]. - ^ load + ^ store ] { #category : 'building' } diff --git a/Druid-Opal/DrOpalExamples.class.st b/Druid-Opal/DrOpalExamples.class.st index cd35e305..88e97aad 100644 --- a/Druid-Opal/DrOpalExamples.class.st +++ b/Druid-Opal/DrOpalExamples.class.st @@ -415,6 +415,30 @@ DrOpalExamples >> methodWithUnknownTypeAnnotationBeforeConcreteTypeAnnotation: a ^ aCollection select: [ :element | element even ] ] +{ #category : 'as yet unclassified' } +DrOpalExamples >> methodWithWhileAndIf [ + + | x | + [ x = 0] + whileFalse: [ + x = nil + ifTrue: [x:=1] + ifFalse: [x:=0] + ] + + +] + +{ #category : 'as yet unclassified' } +DrOpalExamples >> methodWithoutInnerDependencies [ + + | x | + x := true. + self methodWithWhileAndIf + + +] + { #category : 'as yet unclassified' } DrOpalExamples >> methodWritingClassVariable [ From ffe6bea141b54a9dd8f02272c3824577d796fe3f Mon Sep 17 00:00:00 2001 From: palumbon Date: Thu, 23 Jul 2026 10:23:59 -0300 Subject: [PATCH 04/10] Clear context for inlines --- Druid-Opal/DRBytecodeGeneratorTest.class.st | 6 ++++++ Druid-Opal/DRMethodIRGeneratorInline.class.st | 6 ++++++ Druid-Opal/DrOpalExamples.class.st | 18 ++++++++++++++++++ Druid/DRExecutionStack.class.st | 6 ++++++ Druid/DRExecutionState.class.st | 6 ++++++ Druid/DRIRGenerator.class.st | 10 ++++++++-- Druid/DRMessageSend.class.st | 6 +++--- 7 files changed, 53 insertions(+), 5 deletions(-) diff --git a/Druid-Opal/DRBytecodeGeneratorTest.class.st b/Druid-Opal/DRBytecodeGeneratorTest.class.st index ceee09cb..0e351e4a 100644 --- a/Druid-Opal/DRBytecodeGeneratorTest.class.st +++ b/Druid-Opal/DRBytecodeGeneratorTest.class.st @@ -185,6 +185,12 @@ DRBytecodeGeneratorTest >> testBasicControlFlow [ self assert: result equals: 7 ] +{ #category : 'tests' } +DRBytecodeGeneratorTest >> testComplexMethodInline [ + + self assertCompilationFor: #complexInlineMethod fromClass: DrOpalExamples +] + { #category : 'tests' } DRBytecodeGeneratorTest >> testCreateTempVectorInsideLoop [ diff --git a/Druid-Opal/DRMethodIRGeneratorInline.class.st b/Druid-Opal/DRMethodIRGeneratorInline.class.st index 2014dc5d..ef25d59f 100644 --- a/Druid-Opal/DRMethodIRGeneratorInline.class.st +++ b/Druid-Opal/DRMethodIRGeneratorInline.class.st @@ -16,6 +16,12 @@ DRMethodIRGeneratorInline >> allTemporaryInstructions [ ^ executionState allTemporaryInstructions ] +{ #category : 'as yet unclassified' } +DRMethodIRGeneratorInline >> clearTemporaryInstructions [ + + executionState clearTemporaryInstructions +] + { #category : 'accessing' } DRMethodIRGeneratorInline >> currentBasicBlock: aDRBasicBlock [ diff --git a/Druid-Opal/DrOpalExamples.class.st b/Druid-Opal/DrOpalExamples.class.st index 88e97aad..ef5b5c42 100644 --- a/Druid-Opal/DrOpalExamples.class.st +++ b/Druid-Opal/DrOpalExamples.class.st @@ -99,6 +99,15 @@ DrOpalExamples >> blockWithValue: arg [ ^ [ arg ] ] +{ #category : 'as yet unclassified' } +DrOpalExamples >> complexInlineMethod [ + + | x | + x := 0. + x = 0 ifTrue: [ ]. + self whileWithMessageWithBlock: 0 +] + { #category : 'displaying' } DrOpalExamples >> displayString [ @@ -696,3 +705,12 @@ DrOpalExamples >> simpleMethodWithTempAssignment [ ] + +{ #category : 'as yet unclassified' } +DrOpalExamples >> whileWithMessageWithBlock: arg [ + + [arg = nil] + whileTrue: [ + self basicMethodWithBlock: [ dummyVariable := 1 ] + ] +] diff --git a/Druid/DRExecutionStack.class.st b/Druid/DRExecutionStack.class.st index 00310a64..33b5361d 100644 --- a/Druid/DRExecutionStack.class.st +++ b/Druid/DRExecutionStack.class.st @@ -38,6 +38,12 @@ DRExecutionStack >> baseFrame [ ^ stateStack last ] +{ #category : 'as yet unclassified' } +DRExecutionStack >> clearTemporaryInstructions [ + + stateStack do: [ :stack | stack temporaries removeAll ] +] + { #category : 'enumerating' } DRExecutionStack >> detectFrame: aFullBlockClosure [ diff --git a/Druid/DRExecutionState.class.st b/Druid/DRExecutionState.class.st index db3679fe..5efc0397 100644 --- a/Druid/DRExecutionState.class.st +++ b/Druid/DRExecutionState.class.st @@ -31,6 +31,12 @@ DRExecutionState >> baseFrame [ ^ executionStack baseFrame ] +{ #category : 'as yet unclassified' } +DRExecutionState >> clearTemporaryInstructions [ + + executionStack clearTemporaryInstructions +] + { #category : 'accessing' } DRExecutionState >> executionStack [ diff --git a/Druid/DRIRGenerator.class.st b/Druid/DRIRGenerator.class.st index 366739a7..4bfe83bb 100644 --- a/Druid/DRIRGenerator.class.st +++ b/Druid/DRIRGenerator.class.st @@ -1706,8 +1706,14 @@ DRIRGenerator >> messageSendInstructionFor: aRBMessageNode receiver: receiver ar instruction inlineGenerator: inlineGenerator. instruction methodNode: (method ifNotNil: [ method ast ]). - "Set the send as user to the dependecies from inner blocks" - instruction innerDependencies do: [ :i | i addUser: instruction ]. + "Sends with blocks must keep the context (temporaries in the stack) + That is having all outer temporaries in the dependency-user chain. + If not, we don't need them, and the state is clear from previous context + This simplify the inlinings, the reason why the send has a inlineGenerator + " + instruction hasBlockOperand + ifTrue: [ instruction innerDependencies do: [ :i | i addUser: instruction ] ] + ifFalse: [ inlineGenerator clearTemporaryInstructions ]. ^ self addInstruction: instruction from: aRBMessageNode ] diff --git a/Druid/DRMessageSend.class.st b/Druid/DRMessageSend.class.st index 30c5ca9e..da763828 100644 --- a/Druid/DRMessageSend.class.st +++ b/Druid/DRMessageSend.class.st @@ -29,9 +29,9 @@ DRMessageSend >> dependencies [ ] { #category : 'testing' } -DRMessageSend >> hasBlockArgument [ +DRMessageSend >> hasBlockOperand [ - ^ self arguments anySatisfy: [ :arg | arg isDRBlockClosure ] + ^ self operands anySatisfy: [ :arg | arg isDRBlockClosure ] ] { #category : 'optimizations' } @@ -57,7 +57,7 @@ DRMessageSend >> inlineGenerator: anIrGenerator [ { #category : 'inline' } DRMessageSend >> innerDependencies [ - self hasBlockArgument ifFalse: [ ^ #( ) ]. + self hasBlockOperand ifFalse: [ ^ #( ) ]. inlineGenerator ifNil: [ ^ #( ) ]. ^ (inlineGenerator allTemporaryInstructions reject: #isDRBlockClosure) copyWithoutAll: self operands From 17d906d282fba6bb635aa83288f2498744fd8346 Mon Sep 17 00:00:00 2001 From: palumbon Date: Thu, 23 Jul 2026 12:05:19 -0300 Subject: [PATCH 05/10] Lower level of inline for debug --- Druid-Opal/DRLocalVariableInstructionScheluder.class.st | 2 +- Druid/DRInline.class.st | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Druid-Opal/DRLocalVariableInstructionScheluder.class.st b/Druid-Opal/DRLocalVariableInstructionScheluder.class.st index 1540a88f..254c6ba3 100644 --- a/Druid-Opal/DRLocalVariableInstructionScheluder.class.st +++ b/Druid-Opal/DRLocalVariableInstructionScheluder.class.st @@ -67,7 +67,7 @@ DRLocalVariableInstructionScheluder >> storePhiOperands: aDRPhiFunction [ op result isNoResult ]) ifTrue: [ "If they are already temporaries then it is ok" ^ self ]. - aDRPhiFunction operands do: [ :op | op isNullValue ifTrue: [ "1halt." ^self ] ]. +" aDRPhiFunction operands do: [ :op | op isNullValue ifTrue: [ ""1halt."" ^self ] ]." aDRPhiFunction operandsWithoutMe select: [ :op | op isInstruction ] diff --git a/Druid/DRInline.class.st b/Druid/DRInline.class.st index 924a7feb..87a4daa2 100644 --- a/Druid/DRInline.class.st +++ b/Druid/DRInline.class.st @@ -35,7 +35,7 @@ DRInline >> doApply: cfg [ self class enabled ifFalse: [ ^ self ]. - (cfg messageSends takeFirst: 5) do: [ :messageSend | + (cfg messageSends takeFirst: 3) do: [ :messageSend | self inline: messageSend ] ] From 03ce39ff8cf4e737cdb60bb4f9c3b96e59cea6cc Mon Sep 17 00:00:00 2001 From: palumbon Date: Thu, 23 Jul 2026 12:49:17 -0300 Subject: [PATCH 06/10] Push up methods --- Druid-Opal/DRMethodIRGeneratorInline.class.st | 12 ------------ Druid/DRIRGenerator.class.st | 12 ++++++++++++ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/Druid-Opal/DRMethodIRGeneratorInline.class.st b/Druid-Opal/DRMethodIRGeneratorInline.class.st index ef25d59f..7168f1b0 100644 --- a/Druid-Opal/DRMethodIRGeneratorInline.class.st +++ b/Druid-Opal/DRMethodIRGeneratorInline.class.st @@ -10,18 +10,6 @@ Class { #tag : 'Compiler' } -{ #category : 'accessing' } -DRMethodIRGeneratorInline >> allTemporaryInstructions [ - - ^ executionState allTemporaryInstructions -] - -{ #category : 'as yet unclassified' } -DRMethodIRGeneratorInline >> clearTemporaryInstructions [ - - executionState clearTemporaryInstructions -] - { #category : 'accessing' } DRMethodIRGeneratorInline >> currentBasicBlock: aDRBasicBlock [ diff --git a/Druid/DRIRGenerator.class.st b/Druid/DRIRGenerator.class.st index 4bfe83bb..5a09262b 100644 --- a/Druid/DRIRGenerator.class.st +++ b/Druid/DRIRGenerator.class.st @@ -104,6 +104,12 @@ DRIRGenerator >> addInstructionWithNoResultFrom: aNode instructionKind: instruct ^ self addInstruction: instruction from: aNode ] +{ #category : 'accessing' } +DRIRGenerator >> allTemporaryInstructions [ + + ^ executionState allTemporaryInstructions +] + { #category : 'factory' } DRIRGenerator >> allocateTemporaryRegister [ @@ -228,6 +234,12 @@ DRIRGenerator >> buildIfTrue: aCondition then: aBuildingBlock [ trueBranchBasicBlockOut jumpTo: self currentBasicBlock ] ] +{ #category : 'as yet unclassified' } +DRIRGenerator >> clearTemporaryInstructions [ + + executionState clearTemporaryInstructions +] + { #category : 'accessing' } DRIRGenerator >> compiler [ From caec6d18d9231cf67e828cc14d6f0d9075339689 Mon Sep 17 00:00:00 2001 From: palumbon Date: Mon, 27 Jul 2026 18:55:48 -0300 Subject: [PATCH 07/10] Array type + SmallInteger as default integer type --- Druid/DRArray.class.st | 6 ++++++ Druid/DRSignedIntegerType.class.st | 2 +- Druid/DRUnsignedIntegerType.class.st | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/Druid/DRArray.class.st b/Druid/DRArray.class.st index 3cc90734..963ff2ae 100644 --- a/Druid/DRArray.class.st +++ b/Druid/DRArray.class.st @@ -23,3 +23,9 @@ DRArray >> sccpLatticeValueFor: sccp [ ^ sccp bottom ] + +{ #category : 'types' } +DRArray >> typeWithAlreadySeen: aCollection [ + + ^ Array asDRType +] diff --git a/Druid/DRSignedIntegerType.class.st b/Druid/DRSignedIntegerType.class.st index 5c89ecdd..297e4a98 100644 --- a/Druid/DRSignedIntegerType.class.st +++ b/Druid/DRSignedIntegerType.class.st @@ -29,5 +29,5 @@ DRSignedIntegerType >> isUnsigned [ { #category : 'accessing' } DRSignedIntegerType >> systemClass [ - ^ Integer + ^ SmallInteger ] diff --git a/Druid/DRUnsignedIntegerType.class.st b/Druid/DRUnsignedIntegerType.class.st index e63a9b61..2b471e10 100644 --- a/Druid/DRUnsignedIntegerType.class.st +++ b/Druid/DRUnsignedIntegerType.class.st @@ -29,5 +29,5 @@ DRUnsignedIntegerType >> isUnsigned [ { #category : 'accessing' } DRUnsignedIntegerType >> systemClass [ - ^ Integer + ^ SmallInteger ] From 980c20d07fc52fa1f53c1da2abb9ed7567bffe00 Mon Sep 17 00:00:00 2001 From: palumbon Date: Mon, 27 Jul 2026 19:30:15 -0300 Subject: [PATCH 08/10] New scope ids due to temp vectors collitions --- Druid-Opal/DRBytecodeGenerator.class.st | 6 + Druid-Opal/DRInlineScopeTest.class.st | 138 +++++++----------- Druid-Opal/DRMethodIRGenerator.class.st | 24 ++- Druid-Opal/DRMethodIRGeneratorInline.class.st | 6 +- Druid-Opal/DRScope.class.st | 4 +- Druid/DRInstructionFactory.class.st | 14 +- 6 files changed, 94 insertions(+), 98 deletions(-) diff --git a/Druid-Opal/DRBytecodeGenerator.class.st b/Druid-Opal/DRBytecodeGenerator.class.st index 70fea1be..9d59de10 100644 --- a/Druid-Opal/DRBytecodeGenerator.class.st +++ b/Druid-Opal/DRBytecodeGenerator.class.st @@ -364,6 +364,12 @@ DRBytecodeGenerator >> visitMultiply: aDRMultiply [ self sendMessage: #* fromInstruction: aDRMultiply ] +{ #category : 'visiting' } +DRBytecodeGenerator >> visitNegate: aDRNegate [ + + self sendMessage: #negated fromInstruction: aDRNegate +] + { #category : 'ir-to-target' } DRBytecodeGenerator >> visitNoop: aDRNoop [ diff --git a/Druid-Opal/DRInlineScopeTest.class.st b/Druid-Opal/DRInlineScopeTest.class.st index 9f2fd557..0bf955f9 100644 --- a/Druid-Opal/DRInlineScopeTest.class.st +++ b/Druid-Opal/DRInlineScopeTest.class.st @@ -23,7 +23,7 @@ DRInlineScopeTest >> setUp [ { #category : 'tests' } DRInlineScopeTest >> testInlineMethodBlock [ - | cfg blockCFG inlinedBlockScope inlinedMethodScope send | + | cfg blockCFG inlinedBlockScope inlinedMethodScope send vector1 vector2 vector3 vector4 | cfg := self generateDruidIRFor: #methodBlockNested2ReadAndWrite. send := cfg messageSends first. self inline: send. @@ -40,43 +40,35 @@ DRInlineScopeTest >> testInlineMethodBlock [ self assert: send scope inlinedScopes unique equals: inlinedMethodScope. self assert: inlinedMethodScope inlinedScopes unique equals: inlinedBlockScope. + "Temp vectors" + vector1 := { ('0vector0' -> #( #temp1 #temp2 ))} asOrderedDictionary. + vector2 := { ('4vector5' -> #( #tmp1 #aBlock ))} asOrderedDictionary. + vector3 := { ('5vector6' -> #( x ))} asOrderedDictionary. + vector4 := { ('6vector7' -> #( y ))} asOrderedDictionary. + self assert: cfg scope argumentNames isEmpty. - self assert: cfg scope tempVarNames equals: #( temp1 temp2 ). - self assert: cfg scope definedTempVectors equals: { - ('0vector0' -> #( #temp1 #temp2 )) } asOrderedDictionary. + self assert: cfg scope definedTempVectors equals: vector1. self assert: cfg scope copiedVars isEmpty. self assert: send scope isEmpty. self assert: inlinedMethodScope argumentNames isEmpty. - self assert: inlinedMethodScope tempVarNames equals: #( #tmp1 #aBlock ). - self assert: inlinedMethodScope definedTempVectors equals: { - ('0vector2' -> #( #tmp1 #aBlock )) } asOrderedDictionary. - self assert: inlinedMethodScope copiedVars equals: { - ('0vector0' -> #( #temp1 #temp2 )) } asOrderedDictionary. + self assert: inlinedMethodScope definedTempVectors equals: vector2. + self assert: inlinedMethodScope copiedVars equals: vector1. self assert: inlinedBlockScope argumentNames isEmpty. - self assert: inlinedBlockScope tempVarNames equals: #( x ). - self assert: inlinedBlockScope definedTempVectors equals: { - ('0vector3' -> #( x )) } asOrderedDictionary. - self assert: inlinedBlockScope copiedVars equals: { - ('0vector0' -> #( #temp1 #temp2 )). - ('0vector2' -> #( #tmp1 #aBlock )) } asOrderedDictionary. + self assert: inlinedBlockScope definedTempVectors equals: vector3. + self assert: inlinedBlockScope copiedVars equals: vector1, vector2. self assert: blockCFG scope argumentNames isEmpty. - self assert: blockCFG scope tempVarNames equals: #( y ). - self assert: blockCFG scope definedTempVectors equals: { - ('0vector4' -> #( y )) } asOrderedDictionary. - self assert: blockCFG scope copiedVars equals: { - ('0vector0' -> #( #temp1 #temp2 )). - ('0vector2' -> #( #tmp1 #aBlock )). - ('0vector3' -> #( x )) } asOrderedDictionary + self assert: blockCFG scope definedTempVectors equals: vector4. + self assert: blockCFG scope copiedVars equals: vector1, vector2, vector3 ] { #category : 'tests' } DRInlineScopeTest >> testInlineMethodBlockTwoOuterScopes [ - | cfg blockCFG inlinedScope valueMessage blockScope blockReturn send | + | cfg blockCFG inlinedScope valueMessage blockScope blockReturn send vector1 vector2 | cfg := self generateDruidIRFor: #methodBlockTwoOuterScopes. send := cfg messageSends first. self inline: send. @@ -97,20 +89,18 @@ DRInlineScopeTest >> testInlineMethodBlockTwoOuterScopes [ self assert: valueMessage inlineGenerator scope isEmpty. "All temps are inlined in different vectors" + vector1 := {('0vector0' -> #( temp block ))} asOrderedDictionary. + vector2 := {('1vector3' -> #( #arg ))} asOrderedDictionary. + self assert: cfg scope argumentNames isEmpty. - self assert: cfg scope tempVarNames equals: #( temp block ). - self assert: cfg scope definedTempVectors equals: { - ('0vector0' -> #( temp block )) } asOrderedDictionary. + self assert: cfg scope definedTempVectors equals: vector1. self assert: cfg scope copiedVars isEmpty. self assert: send scope isEmpty. self assert: inlinedScope argumentNames isEmpty. - self assert: inlinedScope tempVarNames equals: #( #arg ). - self assert: inlinedScope definedTempVectors equals: { - ('0vector2' -> #( #arg )) } asOrderedDictionary. - self assert: inlinedScope copiedVars equals: { - ('0vector0' -> #( temp block )) } asOrderedDictionary. + self assert: inlinedScope definedTempVectors equals: vector2. + self assert: inlinedScope copiedVars equals: vector1. "Each instruction point to the correct vector" blockReturn := blockCFG lastBasicBlock endInstruction. @@ -122,7 +112,7 @@ DRInlineScopeTest >> testInlineMethodBlockTwoOuterScopes [ { #category : 'tests' } DRInlineScopeTest >> testInlineMethodCollisionTemporaries [ - | cfg inlinedMethodScope1 inlinedBlockScope1 inlinedStores inlinedMethodScope2 inlinedBlockScope2 send1 send2 sendScope1 sendScope2 | + | cfg inlinedMethodScope1 inlinedBlockScope1 inlinedStores inlinedMethodScope2 inlinedBlockScope2 send1 send2 sendScope1 sendScope2 vector1 vector2 vector3 vector4 vector5 | cfg := self generateDruidIRFor: #methodBlockNested2ReadAndWrite. send1 := cfg messageSends first. self inline: send1. @@ -153,49 +143,35 @@ DRInlineScopeTest >> testInlineMethodCollisionTemporaries [ self assert: inlinedBlockScope2 outerScope equals: inlinedMethodScope2. "All temps are inlined in different vectors" + vector1 := {('0vector0' -> #( #temp1 #temp2 ))} asOrderedDictionary. + vector2 := {('4vector5' -> #( #tmp1 #aBlock ))} asOrderedDictionary. + vector3 := {('5vector6' -> #( x ))} asOrderedDictionary. + vector4 := {('8vector9' -> #( #tmp1 #aBlock ))} asOrderedDictionary. + vector5 := {('9vector10' -> #( y ))} asOrderedDictionary. + self assert: cfg scope argumentNames isEmpty. - self assert: cfg scope tempVarNames equals: #( temp1 temp2 ). - self assert: cfg scope definedTempVectors equals: { - ('0vector0' -> #( #temp1 #temp2 )) } asOrderedDictionary. + self assert: cfg scope definedTempVectors equals: vector1. self assert: cfg scope copiedVars isEmpty. self assert: send1 scope isEmpty. self assert: inlinedMethodScope1 argumentNames isEmpty. - self assert: inlinedMethodScope1 tempVarNames equals: #( #tmp1 #aBlock ). - self assert: inlinedMethodScope1 definedTempVectors equals: { - ('0vector2' -> #( #tmp1 #aBlock )) } asOrderedDictionary. - self assert: inlinedMethodScope1 copiedVars equals: { - ('0vector0' -> #( #temp1 #temp2 )) } asOrderedDictionary. + self assert: inlinedMethodScope1 definedTempVectors equals: vector2. + self assert: inlinedMethodScope1 copiedVars equals: vector1. self assert: inlinedBlockScope1 argumentNames isEmpty. - self assert: inlinedBlockScope1 tempVarNames equals: #( x ). - self assert: inlinedBlockScope1 definedTempVectors equals: { - ('0vector3' -> #( x )) } asOrderedDictionary. - self assert: inlinedBlockScope1 copiedVars equals: { - ('0vector0' -> #( #temp1 #temp2 )). - ('0vector2' -> #( #tmp1 #aBlock )) } asOrderedDictionary. + self assert: inlinedBlockScope1 definedTempVectors equals: vector3. + self assert: inlinedBlockScope1 copiedVars equals: vector1, vector2. self assert: send2 scope isEmpty. self assert: inlinedMethodScope2 argumentNames isEmpty. - self assert: inlinedMethodScope2 tempVarNames equals: #( #tmp1 #aBlock ). - self assert: inlinedMethodScope2 definedTempVectors equals: { - ('0vector5' -> #( #tmp1 #aBlock )) } asOrderedDictionary. - self assert: inlinedMethodScope2 copiedVars equals: { - ('0vector0' -> #( #temp1 #temp2 )). - ('0vector2' -> #( #tmp1 #aBlock )). - ('0vector3' -> #( x )) } asOrderedDictionary. + self assert: inlinedMethodScope2 definedTempVectors equals: vector4. + self assert: inlinedMethodScope2 copiedVars equals: vector1, vector2, vector3. self assert: inlinedBlockScope2 argumentNames isEmpty. - self assert: inlinedBlockScope2 tempVarNames equals: #( y ). - self assert: inlinedBlockScope2 definedTempVectors equals: { - ('0vector6' -> #( y )) } asOrderedDictionary. - self assert: inlinedBlockScope2 copiedVars equals: { - ('0vector0' -> #( #temp1 #temp2 )). - ('0vector2' -> #( #tmp1 #aBlock )). - ('0vector3' -> #( x )). - ('0vector5' -> #( #tmp1 #aBlock )) } asOrderedDictionary. + self assert: inlinedBlockScope2 definedTempVectors equals: vector5. + self assert: inlinedBlockScope2 copiedVars equals: vector1, vector2, vector3, vector4. "Each instruction point to the correct vector" inlinedStores := cfg instructions select: [ :i | @@ -209,7 +185,7 @@ DRInlineScopeTest >> testInlineMethodCollisionTemporaries [ { #category : 'tests' } DRInlineScopeTest >> testInlineMultiMethods [ - | cfg block1CFG block2CFG inlinedMethod1Scope send inlinedMethod2Scope | + | cfg block1CFG block2CFG inlinedMethod1Scope send inlinedMethod2Scope vector1 vector2a vector3a vector2b vector3b | compilerCompiler irGenerator typeSystem: DRPragmaBasedTypeSystem new. cfg := self generateDruidIRFor: #methodWithMultipleTypeAnnotations:. send := cfg messageSends first. @@ -233,45 +209,35 @@ DRInlineScopeTest >> testInlineMultiMethods [ self assert: send scope outerScope equals: cfg scope. "Variables" - self assert: cfg scope argumentNames equals: #(aCollection). + vector1 := {('0vector0' -> #( #aCollection ))} asOrderedDictionary. self assert: cfg scope tempVarNames isEmpty. - self assert: cfg scope definedTempVectors equals: { - ('0vector0' -> #( #aCollection )) } asOrderedDictionary. + self assert: cfg scope definedTempVectors equals: vector1. self assert: cfg scope copiedVars isEmpty. self assert: send scope isEmpty. "Inline 1" + vector2a := {('3vector4' -> #( #newCollection #element #selectBlock ))} asOrderedDictionary. self assert: inlinedMethod1Scope argumentNames isEmpty. - self assert: inlinedMethod1Scope tempVarNames equals: #( #newCollection #element #selectBlock ). - self assert: inlinedMethod1Scope definedTempVectors equals: { - ('0vector2' -> #( #newCollection #element #selectBlock )) } asOrderedDictionary. - self assert: inlinedMethod1Scope copiedVars equals: { - ('0vector0' -> #( #aCollection )) } asOrderedDictionary. + self assert: inlinedMethod1Scope definedTempVectors equals: vector2a. + self assert: inlinedMethod1Scope copiedVars equals: vector1. + vector3a := {('4vector6' -> #( #index ))} asOrderedDictionary. self assert: block1CFG scope argumentNames equals: #( index ). self assert: block1CFG scope tempVarNames isEmpty. - self assert: block1CFG scope definedTempVectors equals: { - ('0vector3' -> #( #index )) } asOrderedDictionary. - self assert: block1CFG scope copiedVars equals: { - ('0vector0' -> #( #aCollection )). - ('0vector2' -> #( #newCollection #element #selectBlock )) } asOrderedDictionary. + self assert: block1CFG scope definedTempVectors equals: vector3a. + self assert: block1CFG scope copiedVars equals: vector1, vector2a. "Inline 2" + vector2b := {('3vector11' -> #( #newCollection #aBlock ))} asOrderedDictionary. self assert: inlinedMethod2Scope argumentNames isEmpty. - self assert: inlinedMethod2Scope tempVarNames equals: #( #newCollection #aBlock ). - self assert: inlinedMethod2Scope definedTempVectors equals: { - ('0vector2' -> #( #newCollection #aBlock )) } asOrderedDictionary. - self assert: inlinedMethod2Scope copiedVars equals: { - ('0vector0' -> #( #aCollection )) } asOrderedDictionary. + self assert: inlinedMethod2Scope definedTempVectors equals: vector2b. + self assert: inlinedMethod2Scope copiedVars equals: vector1. - self assert: block2CFG scope argumentNames equals: #( #each ). + vector3b := {('11vector13' -> #( #each ))} asOrderedDictionary. self assert: block2CFG scope tempVarNames isEmpty. - self assert: block2CFG scope definedTempVectors equals: { - ('0vector3' -> #( #each )) } asOrderedDictionary. - self assert: block2CFG scope copiedVars equals: { - ('0vector0' -> #( #aCollection )). - ('0vector2' -> #( #newCollection #aBlock )) } asOrderedDictionary. + self assert: block2CFG scope definedTempVectors equals: vector3b. + self assert: block2CFG scope copiedVars equals: vector1, vector2b. ] diff --git a/Druid-Opal/DRMethodIRGenerator.class.st b/Druid-Opal/DRMethodIRGenerator.class.st index 25f07299..ecaa57b1 100644 --- a/Druid-Opal/DRMethodIRGenerator.class.st +++ b/Druid-Opal/DRMethodIRGenerator.class.st @@ -44,13 +44,14 @@ DRMethodIRGenerator >> blockClosureGenerator [ | blockGenerator | blockGenerator := DRBlockIRGenerator new. + blockGenerator instructionFactory: self instructionFactory. - "Review this decision..." + "For debugging?" blockGenerator variableFrame: self topFrame copy. blockGenerator ir scope outerScope: self scope; - id: self scope id + 1. + id: self instructionFactory allocateScopeId. ^ blockGenerator ] @@ -65,8 +66,7 @@ DRMethodIRGenerator >> finishCodeInterpretation: lastFrame [ DRMethodIRGenerator >> initialize [ super initialize. - controlFlowGraph scope: DRScope new. - + controlFlowGraph scope: self newScope. typeSystem := DRPragmaBasedTypeSystem new ] @@ -299,6 +299,16 @@ DRMethodIRGenerator >> newCFG [ ^ DRMethodControlFlowGraph new ] +{ #category : 'instance creation' } +DRMethodIRGenerator >> newScope [ + + ^ DRScope new + outerScope: self outerScope; + id: self instructionFactory allocateScopeId; + yourself. + +] + { #category : 'accessing' } DRMethodIRGenerator >> numberOfArguments: aValue [ @@ -307,6 +317,12 @@ DRMethodIRGenerator >> numberOfArguments: aValue [ self push: (self currentBasicBlock loadArgument: i) ]" ] +{ #category : 'accessing' } +DRMethodIRGenerator >> outerScope [ + + ^ nil +] + { #category : 'frame-access' } DRMethodIRGenerator >> popFrameMergingDeferredReturns [ diff --git a/Druid-Opal/DRMethodIRGeneratorInline.class.st b/Druid-Opal/DRMethodIRGeneratorInline.class.st index 7168f1b0..a2655ca0 100644 --- a/Druid-Opal/DRMethodIRGeneratorInline.class.st +++ b/Druid-Opal/DRMethodIRGeneratorInline.class.st @@ -29,11 +29,7 @@ DRMethodIRGeneratorInline >> finishCodeInterpretation: lastFrame [ { #category : 'scopes' } DRMethodIRGeneratorInline >> generateScope [ - scope := DRScope new - outerScope: self outerScope; - id: self outerScope id + 1; - yourself. - + scope := self newScope. self outerScope addInlinedScope: scope ] diff --git a/Druid-Opal/DRScope.class.st b/Druid-Opal/DRScope.class.st index 9e811604..56879ab4 100644 --- a/Druid-Opal/DRScope.class.st +++ b/Druid-Opal/DRScope.class.st @@ -195,7 +195,9 @@ DRScope >> tempVarNames [ DRScope >> tempVectorName [ "the name of the tempVector is not a valid name of a temp variable This way we avoid name clashes " - ^'0vector', id asString + | parent | + parent := outerScope ifNotNil: [ outerScope id asString ] ifNil: [ '0' ]. + ^parent, 'vector', id asString ] { #category : 'query' } diff --git a/Druid/DRInstructionFactory.class.st b/Druid/DRInstructionFactory.class.st index 88ac570c..68b055a8 100644 --- a/Druid/DRInstructionFactory.class.st +++ b/Druid/DRInstructionFactory.class.st @@ -2,7 +2,8 @@ Class { #name : 'DRInstructionFactory', #superclass : 'Object', #instVars : [ - 'nextAvailableRegister' + 'nextAvailableRegister', + 'nextAvailableScopeId' ], #category : 'Druid-IR', #package : 'Druid', @@ -17,6 +18,14 @@ DRInstructionFactory >> add: operand1 to: operand2 [ result: self allocateTemporaryRegister ] +{ #category : 'as yet unclassified' } +DRInstructionFactory >> allocateScopeId [ + | scopeId | + scopeId := nextAvailableScopeId. + nextAvailableScopeId := nextAvailableScopeId + 1. + ^ scopeId +] + { #category : 'factory' } DRInstructionFactory >> allocateTemporaryRegister [ @@ -125,7 +134,8 @@ DRInstructionFactory >> greater: operand1 than: operand2 [ DRInstructionFactory >> initialize [ super initialize. - nextAvailableRegister := 0 + nextAvailableRegister := 0. + nextAvailableScopeId := 0 ] { #category : 'initialization' } From 6e7c02f0dcd9f841feeeab00d41b1f3f0afd75db Mon Sep 17 00:00:00 2001 From: palumbon Date: Thu, 30 Jul 2026 11:01:52 -0300 Subject: [PATCH 09/10] Fix double inline loop accessing method argument issue --- Druid-Opal/DRInlineMethodTest.class.st | 18 ++++++++++++++++++ Druid-Opal/DRMethodIRGenerator.class.st | 6 ++++++ Druid-Opal/DrOpalExamples.class.st | 9 +++++++++ 3 files changed, 33 insertions(+) diff --git a/Druid-Opal/DRInlineMethodTest.class.st b/Druid-Opal/DRInlineMethodTest.class.st index f8ebea7c..2faabf32 100644 --- a/Druid-Opal/DRInlineMethodTest.class.st +++ b/Druid-Opal/DRInlineMethodTest.class.st @@ -65,6 +65,24 @@ DRInlineMethodTest >> testInlineMethodKeepsMessageSendIfTypeIsUnknown [ self assert: (cfg messageSends anySatisfy: [ :ms | ms originAST = messageSend originAST ]) ] +{ #category : 'tests - type systems' } +DRInlineMethodTest >> testInlineMethodWithInlinedLoops [ + + | cfg messageSend selector blockSend | + cfg := self generateDruidIRFor: #doubleWhileWithBlock:. + selector := #ifFalse:. + messageSend := cfg messageSends unique. + + self assert: messageSend selector equals: selector. + + cfg applyOptimisation: DRInline new. + + blockSend := cfg messageSends unique. + + self assert: blockSend receiver isLoadArgument. + self assert: blockSend receiver argNum equals: 1 +] + { #category : 'tests' } DRInlineMethodTest >> testInlineMethodWithMultiplePossibleTypesInlinesAllPossibleMethods [ diff --git a/Druid-Opal/DRMethodIRGenerator.class.st b/Druid-Opal/DRMethodIRGenerator.class.st index ecaa57b1..e5213c0a 100644 --- a/Druid-Opal/DRMethodIRGenerator.class.st +++ b/Druid-Opal/DRMethodIRGenerator.class.st @@ -407,6 +407,12 @@ DRMethodIRGenerator >> setupCFGScope: aCodeNode [ node: aCodeNode ] +{ #category : 'as yet unclassified' } +DRMethodIRGenerator >> temporariesListfrom: aNode [ + + ^ aNode temporaryNames. +] + { #category : 'accessing' } DRMethodIRGenerator >> typeSystem: aDRTypeSystem [ diff --git a/Druid-Opal/DrOpalExamples.class.st b/Druid-Opal/DrOpalExamples.class.st index ef5b5c42..8b9d25ff 100644 --- a/Druid-Opal/DrOpalExamples.class.st +++ b/Druid-Opal/DrOpalExamples.class.st @@ -120,6 +120,15 @@ DrOpalExamples >> doubleOf: n [ ^ n * 2 ] +{ #category : 'as yet unclassified' } +DrOpalExamples >> doubleWhileWithBlock: aBlock [ + | x y | + [ + [ x > y ifFalse: [aBlock value: x]. + x > y] whileTrue: [x := x + 1]. + x < y] whileTrue: [y := y + 1] +] + { #category : 'as yet unclassified' } DrOpalExamples >> empty [ ] From 174d80f955577aa6e5c700a1e950c59f42c9a2df Mon Sep 17 00:00:00 2001 From: Nahuel Palumbo Date: Tue, 4 Aug 2026 16:25:05 +0200 Subject: [PATCH 10/10] Commented code Co-authored-by: Guille Polito Co-authored-by: Nahuel Palumbo --- Druid-Opal/DRLocalVariableInstructionScheluder.class.st | 2 -- Druid-Opal/DRMethodIRGenerator.class.st | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/Druid-Opal/DRLocalVariableInstructionScheluder.class.st b/Druid-Opal/DRLocalVariableInstructionScheluder.class.st index 254c6ba3..f1aec022 100644 --- a/Druid-Opal/DRLocalVariableInstructionScheluder.class.st +++ b/Druid-Opal/DRLocalVariableInstructionScheluder.class.st @@ -67,13 +67,11 @@ DRLocalVariableInstructionScheluder >> storePhiOperands: aDRPhiFunction [ op result isNoResult ]) ifTrue: [ "If they are already temporaries then it is ok" ^ self ]. -" aDRPhiFunction operands do: [ :op | op isNullValue ifTrue: [ ""1halt."" ^self ] ]." aDRPhiFunction operandsWithoutMe select: [ :op | op isInstruction ] thenDo: [ :op | | storeInst | - "op isPhiFunction ifTrue: [1halt. ^ self ]." storeInst := DRStoreTemporaryVariable operands: { variableName. diff --git a/Druid-Opal/DRMethodIRGenerator.class.st b/Druid-Opal/DRMethodIRGenerator.class.st index e5213c0a..5e8cf293 100644 --- a/Druid-Opal/DRMethodIRGenerator.class.st +++ b/Druid-Opal/DRMethodIRGenerator.class.st @@ -410,7 +410,7 @@ DRMethodIRGenerator >> setupCFGScope: aCodeNode [ { #category : 'as yet unclassified' } DRMethodIRGenerator >> temporariesListfrom: aNode [ - ^ aNode temporaryNames. + ^ aNode temporaryNames ] { #category : 'accessing' }