Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
3 changes: 3 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,6 +66,7 @@ DRLocalVariableInstructionScheluder >> storePhiOperands: aDRPhiFunction [
(aDRPhiFunction operandsWithoutMe allSatisfy: [ :op |
op result isNoResult ]) ifTrue: [ "If they are already temporaries then it is ok"
^ self ].


aDRPhiFunction operandsWithoutMe
select: [ :op | op isInstruction ]
Expand Down
Loading
Loading