Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions Druid-Opal/DRBytecodeGenerator.class.st
Original file line number Diff line number Diff line change
Expand Up @@ -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 [

Expand Down
42 changes: 20 additions & 22 deletions Druid-Opal/DRBytecodeGeneratorTest.class.st
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -199,12 +179,18 @@ 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
]

{ #category : 'tests' }
DRBytecodeGeneratorTest >> testComplexMethodInline [

self assertCompilationFor: #complexInlineMethod fromClass: DrOpalExamples
]

{ #category : 'tests' }
DRBytecodeGeneratorTest >> testCreateTempVectorInsideLoop [

Expand All @@ -223,6 +209,12 @@ DRBytecodeGeneratorTest >> testMethodEmpty [
self assertCompilationFor: #exampleEmptyMethod fromClass: OCOpalExamples
]

{ #category : 'tests' }
DRBytecodeGeneratorTest >> testMethodEmptyIf [

self assertCompilationFor: #emptyIf fromClass: DrOpalExamples
]

{ #category : 'tests' }
DRBytecodeGeneratorTest >> testMethodInline [

Expand Down Expand Up @@ -252,6 +244,12 @@ DRBytecodeGeneratorTest >> testMethodInlineFailsWithUnresolvedNonLocalReturns [
self assert: (err messageText beginsWith: 'Non-local return') ]
]

{ #category : 'tests' }
DRBytecodeGeneratorTest >> testMethodInlineLoop [

self assertCompilationFor: #methodWithoutInnerDependencies fromClass: DrOpalExamples
]

{ #category : 'tests' }
DRBytecodeGeneratorTest >> testMethodInlineObjectAccessInstVar [

Expand Down
18 changes: 18 additions & 0 deletions Druid-Opal/DRInlineMethodTest.class.st
Original file line number Diff line number Diff line change
Expand Up @@ -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 [

Expand Down
138 changes: 52 additions & 86 deletions Druid-Opal/DRInlineScopeTest.class.st
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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.
Expand All @@ -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.
Expand All @@ -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.
Expand Down Expand Up @@ -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 |
Expand All @@ -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.
Expand All @@ -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.


]
4 changes: 2 additions & 2 deletions Druid-Opal/DRInstructionsTest.class.st
Original file line number Diff line number Diff line change
Expand Up @@ -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).

Expand Down
5 changes: 5 additions & 0 deletions Druid-Opal/DRLocalVariableInstructionScheluder.class.st
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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 ] ]."
Comment thread
PalumboN marked this conversation as resolved.
Outdated

aDRPhiFunction operandsWithoutMe
select: [ :op | op isInstruction ]
thenDo: [ :op |
| storeInst |
"op isPhiFunction ifTrue: [1halt. ^ self ]."
Comment thread
PalumboN marked this conversation as resolved.
Outdated
storeInst := DRStoreTemporaryVariable
operands: {
variableName.
Expand Down
Loading
Loading