VM Maker: VMMaker-dtl.338.mcz

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view
|

VM Maker: VMMaker-dtl.338.mcz

commits-2
 
David T. Lewis uploaded a new version of VMMaker to project VM Maker:
http://source.squeak.org/VMMaker/VMMaker-dtl.338.mcz

==================== Summary ====================

Name: VMMaker-dtl.338
Author: dtl
Time: 20 January 2014, 7:13:23.86 pm
UUID: 29946156-9015-45c9-83a2-c12d43f67ece
Ancestors: VMMaker-dtl.337

VMMaker 4.12.14

C translation updates from oscog required for case statement code generation, e.g. shorten:toIndexableSize code generation.

Add shorten:toIndexableSize from oscog, presumed working for NewObjectMemory but not verified for ClassicObjectMemory

=============== Diff against VMMaker-dtl.337 ===============

Item was added:
+ ----- Method: BlockNode>>isPotentialCCaseLabel:in: (in category '*VMMaker-C translation') -----
+ isPotentialCCaseLabel: stmt in: aTMethod
+ (stmt isVariableNode
+ or: [stmt isLiteralNode
+ and: [stmt isConstantNumber or: [stmt literalValue isSymbol]]]) ifTrue:
+ [^true].
+ stmt isMessageNode ifTrue:
+ [| selector method |
+ selector := stmt selector key.
+ (#(* + -) includes: selector) ifTrue:
+ [^(self isPotentialCCaseLabel: stmt receiver in: aTMethod)
+   and: [self isPotentialCCaseLabel: stmt arguments first in: aTMethod]].
+
+ (selector = #asSymbol
+  and: [stmt receiver isLiteralNode
+  and: [stmt receiver literalValue isSymbol]]) ifTrue:
+ [^true].
+
+ (stmt arguments isEmpty
+  and: [method := (aTMethod definingClass whichClassIncludesSelector: selector) >> selector.
+   (method isQuick
+ or: [(method literalAt: 1) isInteger
+ and: [method numLiterals = 3]])
+   and: [(aTMethod definingClass basicNew perform: selector) isInteger]]) ifTrue:
+ [^true]].
+ ^false!

Item was added:
+ ----- Method: BlockNode>>isPotentialCCaseLabelIn: (in category '*VMMaker-C translation') -----
+ isPotentialCCaseLabelIn: aTMethod
+ | stmt |
+ statements size ~= 1 ifTrue: [^false].
+ stmt := statements first.
+ ^self isPotentialCCaseLabel: stmt in: aTMethod!

Item was added:
+ ----- Method: BraceNode>>asTranslatorNodeIn: (in category '*VMMaker-C translation') -----
+ asTranslatorNodeIn: aTMethod
+ "make a CCodeGenerator equivalent of me"
+ self assert: (elements allSatisfy:
+ [:elem|
+ elem isMessageNode
+ and: [elem selector key = #->
+ and: [elem receiver isBlockNode
+ and: [elem arguments first isBlockNode
+ and: [elem receiver isPotentialCCaseLabelIn: aTMethod]]]]]).
+ ^TBraceCaseNode new
+ caseLabels: (elements collect: [:elem| elem receiver asTranslatorNodeIn: aTMethod]);
+ cases: (elements collect: [:elem| elem arguments first asTranslatorNodeIn: aTMethod]);
+ comment: comment!

Item was added:
+ ----- Method: CCodeGenerator>>isBuiltinSelector: (in category 'utilities') -----
+ isBuiltinSelector: sel
+ "Answer true if the given selector is one of the builtin selectors."
+
+ ^(self isKernelSelector: sel) or: [translationDict includesKey: sel]!

Item was added:
+ ----- Method: CCodeGenerator>>isKernelSelector: (in category 'utilities') -----
+ isKernelSelector: sel
+ "Answer true if the given selector is one of the kernel selectors that are implemented as macros."
+
+ ^(#(error:
+ oopAt: oopAt:put: oopAtPointer: oopAtPointer:put:
+ byteAt: byteAt:put: byteAtPointer: byteAtPointer:put:
+ shortAt: shortAt:put: shortAtPointer: shortAtPointer:put:
+ intAt: intAt:put: intAtPointer: intAtPointer:put:
+ longAt: longAt:put: longAtPointer: longAtPointer:put:
+ longLongAt: longLongAt:put: longLongAtPointer: longLongAtPointer:put:
+ fetchFloatAt:into: storeFloatAt:from:
+ fetchFloatAtPointer:into: storeFloatAtPointer:from:
+ fetchSingleFloatAt:into: storeSingleFloatAt:from:
+ fetchSingleFloatAtPointer:into: storeSingleFloatAtPointer:from:
+ pointerForOop: oopForPointer:
+ cCoerce:to: cCoerceSimple:to:)
+ includes: sel)!

Item was added:
+ ----- Method: ClassicObjectMemory>>shorten:toIndexableSize: (in category 'allocation') -----
+ shorten: obj toIndexableSize: nSlots
+ "Currently this works for pointer objects only, and is almost certainly wrong for 64 bits."
+ | deltaBytes desiredLength fixedFields fmt hdr totalLength |
+ (self isPointersNonInt: obj) ifFalse:
+ [^obj].
+ hdr := self baseHeader: obj.
+ fmt := self formatOfHeader: hdr.
+ totalLength := self lengthOf: obj baseHeader: hdr format: fmt.
+ fixedFields := self fixedFieldsOf: obj format: fmt length: totalLength.
+
+ self cCode: '  printf("fixedFields is %d\n", fixedFields); fflush(stdout)  '.
+ self cCode: '  printf("nSlots is %d\n", nSlots); fflush(stdout)  '.
+
+ desiredLength := fixedFields + nSlots.
+ deltaBytes := (totalLength - desiredLength) * self bytesPerWord.
+
+ self cCode: '  printf("desiredLength is %d\n", desiredLength); fflush(stdout)  '.
+ self cCode: '  printf("deltaBytes is %d\n", deltaBytes); fflush(stdout)  '.
+
+ self setSizeOfFree: obj + self baseHeaderSize + (desiredLength * self bytesPerWord)
+ to: deltaBytes.
+ (self headerType: obj) caseOf: {
+ [HeaderTypeSizeAndClass] ->
+ [
+ self cCode: '  printf("HeaderTypeSizeAndClass\n"); fflush(stdout)  '.
+
+ self longAt: obj put: hdr - deltaBytes].
+ [HeaderTypeClass] ->
+ [
+ self cCode: '  printf("HeaderTypeClass\n"); fflush(stdout)  '.
+
+ self longAt: obj put: ((hdr bitClear: self sizeMask) bitOr: (hdr bitAnd: self sizeMask) - deltaBytes)].
+ [HeaderTypeShort] ->
+ [
+ self cCode: '  printf("HeaderTypeShort\n"); fflush(stdout)  '.
+
+ self longAt: obj put: ((hdr bitClear: self sizeMask) bitOr: (hdr bitAnd: self sizeMask) - deltaBytes)] }.
+ ^obj!

Item was added:
+ ----- Method: NewObjectMemory>>shorten:toIndexableSize: (in category 'allocation') -----
+ shorten: obj toIndexableSize: nSlots
+ "Currently this works for pointer objects only, and is almost certainly wrong for 64 bits."
+ | deltaBytes desiredLength fixedFields fmt hdr totalLength |
+ (self isPointersNonImm: obj) ifFalse:
+ [^obj].
+ hdr := self baseHeader: obj.
+ fmt := self formatOfHeader: hdr.
+ totalLength := self lengthOf: obj baseHeader: hdr format: fmt.
+ fixedFields := self fixedFieldsOf: obj format: fmt length: totalLength.
+ desiredLength := fixedFields + nSlots.
+ deltaBytes := (totalLength - desiredLength) * self bytesPerWord.
+ obj + self baseHeaderSize + (totalLength * self bytesPerWord) = freeStart
+ ifTrue: "Shortening the last object.  Need to reduce freeStart."
+ [self maybeFillWithAllocationCheckFillerFrom: obj + self baseHeaderSize + (desiredLength * self bytesPerWord) to: freeStart.
+ freeStart := obj + self baseHeaderSize + (desiredLength * self bytesPerWord)]
+ ifFalse: "Shortening some interior object.  Need to create a free block."
+ [self setSizeOfFree: obj + self baseHeaderSize + (desiredLength * self bytesPerWord)
+ to: deltaBytes].
+ (self headerType: obj) caseOf: {
+ [HeaderTypeSizeAndClass] ->
+ [self longAt: obj put: hdr - deltaBytes].
+ [HeaderTypeClass] ->
+ [self longAt: obj put: ((hdr bitClear: self sizeMask) bitOr: (hdr bitAnd: self sizeMask) - deltaBytes)].
+ [HeaderTypeShort] ->
+ [self longAt: obj put: ((hdr bitClear: self sizeMask) bitOr: (hdr bitAnd: self sizeMask) - deltaBytes)] }.
+ ^obj!

Item was added:
+ ----- Method: ObjectMemory>>shorten:toIndexableSize: (in category 'allocation') -----
+ shorten: obj toIndexableSize: nSlots
+ "Shorten the length of a pointer object to nSlots, marking free memory and adjusting
+ end of memory as required."
+
+ self subclassResponsibility!

Item was added:
+ ----- Method: TAssignmentNode>>nodesDo:parent: (in category 'enumerating') -----
+ nodesDo: aBlock parent: parent
+ "Apply aBlock to all nodes in the receiver with each node's parent.
+ N.B. This is assumed to be bottom-up, leaves first."
+ variable nodesDo: aBlock parent: self.
+ expression nodesDo: aBlock parent: self.
+ aBlock value: self value: parent!

Item was added:
+ ----- Method: TAssignmentNode>>structTargetKindIn: (in category 'testing') -----
+ structTargetKindIn: aCodeGen
+ "Answer if the recever evaluates to a struct or struct pointer
+ and hence can be dereferenced using . or ->.  Answer any of
+ #struct #pointer or nil"
+ ^variable structTargetKindIn: aCodeGen!

Item was added:
+ TParseNode subclass: #TBraceCaseNode
+ instanceVariableNames: 'caseLabels cases'
+ classVariableNames: ''
+ poolDictionaries: ''
+ category: 'VMMaker-Translation to C'!

Item was added:
+ ----- Method: TBraceCaseNode>>bindVariableUsesIn: (in category 'transformations') -----
+ bindVariableUsesIn: aDictionary
+
+ caseLabels := caseLabels collect: [:node| node bindVariableUsesIn: aDictionary].
+ cases := cases collect: [:node| node bindVariableUsesIn: aDictionary]!

Item was added:
+ ----- Method: TBraceCaseNode>>bindVariableUsesIn:andConstantFoldIf:in: (in category 'transformations') -----
+ bindVariableUsesIn: aDictionary andConstantFoldIf: constantFold in: codeGen
+ "Answer either the receiver, if it contains no references to the given variables, or a new node with the given variables rebound."
+ | newCaseLabels newCases |
+ newCaseLabels := caseLabels collect: [:node| node bindVariableUsesIn: aDictionary andConstantFoldIf: constantFold in: codeGen].
+ newCases := cases collect: [:node| node bindVariableUsesIn: aDictionary andConstantFoldIf: constantFold in: codeGen].
+ ^(newCaseLabels = caseLabels
+   and: [newCases = cases])
+ ifTrue: [self]
+ ifFalse: [self shallowCopy
+ caseLabels: newCaseLabels;
+ cases: newCases;
+ yourself]!

Item was added:
+ ----- Method: TBraceCaseNode>>bindVariablesIn: (in category 'transformations') -----
+ bindVariablesIn: aDictionary
+
+ caseLabels := caseLabels collect: [:node| node bindVariablesIn: aDictionary].
+ cases := cases collect: [:node| node bindVariablesIn: aDictionary]!

Item was added:
+ ----- Method: TBraceCaseNode>>caseLabels (in category 'accessing') -----
+ caseLabels
+ "Answer the value of caseLabels"
+
+ ^ caseLabels!

Item was added:
+ ----- Method: TBraceCaseNode>>caseLabels: (in category 'accessing') -----
+ caseLabels: anObject
+ "Set the value of caseLabels"
+
+ caseLabels := anObject!

Item was added:
+ ----- Method: TBraceCaseNode>>cases (in category 'accessing') -----
+ cases
+ "Answer the value of cases"
+
+ ^ cases!

Item was added:
+ ----- Method: TBraceCaseNode>>cases: (in category 'accessing') -----
+ cases: anObject
+ "Set the value of cases"
+
+ cases := anObject!

Item was added:
+ ----- Method: TBraceCaseNode>>copyTree (in category 'copying') -----
+ copyTree
+
+ self flag: #FIXME. "adopt from oscog - get rid of copyTree,  use postCopy instead"
+ ^self copy
+ !

Item was added:
+ ----- Method: TBraceCaseNode>>nodesDo: (in category 'enumerating') -----
+ nodesDo: aBlock
+ "Apply aBlock to all nodes in the receiver.
+ N.B. This is assumed to be bottom-up, leaves first."
+ caseLabels do:
+ [:node| node nodesDo: aBlock].
+ cases do:
+ [:node| node nodesDo: aBlock].
+ aBlock value: self!

Item was added:
+ ----- Method: TBraceCaseNode>>nodesDo:parent: (in category 'enumerating') -----
+ nodesDo: aBlock parent: parent
+ "Apply aBlock to all nodes in the receiver with each node's parent.
+ N.B. This is assumed to be bottom-up, leaves first."
+ caseLabels do:
+ [:node| node nodesDo: aBlock parent: self.].
+ cases do:
+ [:node| node nodesDo: aBlock parent: self].
+ aBlock value: self value: parent!

Item was added:
+ ----- Method: TBraceCaseNode>>nodesDo:unless: (in category 'enumerating') -----
+ nodesDo: aBlock unless: cautionaryBlock
+
+ (cautionaryBlock value: self) ifTrue: [^self].
+ caseLabels do:
+ [:node| node nodesDo: aBlock unless: cautionaryBlock].
+ cases do:
+ [:node| node nodesDo: aBlock unless: cautionaryBlock].
+ aBlock value: self!

Item was added:
+ ----- Method: TBraceCaseNode>>replaceNodesIn: (in category 'enumerating') -----
+ replaceNodesIn: aDictionary
+
+ ^aDictionary at: self ifAbsent: [
+ caseLabels := caseLabels collect: [:node| node replaceNodesIn: aDictionary].
+ cases := cases collect: [:node| node replaceNodesIn: aDictionary].
+ self]!

Item was added:
+ ----- Method: TCaseStmtNode>>nodesDo:parent: (in category 'enumerating') -----
+ nodesDo: aBlock parent: parent
+ "Apply aBlock to all nodes in the receiver with each node's parent.
+ N.B. This is assumed to be bottom-up, leaves first."
+ expression nodesDo: aBlock parent: self.
+ cases do: [:c| c nodesDo: aBlock parent: self].
+ aBlock value: self value: parent!

Item was added:
+ ----- Method: TConstantNode>>nodesDo:parent: (in category 'enumerating') -----
+ nodesDo: aBlock parent: parent
+ "Apply aBlock to all nodes in the receiver with each node's parent.
+ N.B. This is assumed to be bottom-up, leaves first."
+ aBlock value: self value: parent!

Item was added:
+ ----- Method: TGoToNode>>nodesDo:parent: (in category 'enumerating') -----
+ nodesDo: aBlock parent: parent
+ aBlock value: self value: parent!

Item was added:
+ ----- Method: TLabeledCommentNode>>nodesDo:parent: (in category 'enumerating') -----
+ nodesDo: aBlock parent: parent
+ aBlock value: self value: parent!

Item was changed:
  Object subclass: #TMethod
+ instanceVariableNames: 'selector returnType args locals declarations primitive parseTree labels possibleSideEffectsCache complete export static sharedLabel sharedCase comment definingClass globalStructureBuildMethodHasFoo canAsmLabel mustAsmLabel properties cascadeVariableNumber extraVariableNumber'
- instanceVariableNames: 'selector returnType args locals declarations primitive parseTree labels possibleSideEffectsCache complete export static sharedLabel sharedCase comment definingClass globalStructureBuildMethodHasFoo canAsmLabel mustAsmLabel properties cascadeVariableNumber'
  classVariableNames: 'CaseStatements'
  poolDictionaries: ''
  category: 'VMMaker-Translation to C'!
 
  !TMethod commentStamp: 'dtl 9/15/2008 09:06' prior: 0!
  A TMethod is a translation method, representing a MethodNode that is to be translated to C source. It has a parseTree of translation nodes that mirrors the parse tree of the corresponding Smalltalk method.!

Item was added:
+ ----- Method: TMethod>>buildSwitchStmt:parent: (in category 'transformations') -----
+ buildSwitchStmt: aSendNode parent: parentNode
+ "Build a switch statement node for the given send of caseOf: or caseOf:otherwise:."
+ | switch |
+ switch := TSwitchStmtNode new
+ expression: aSendNode receiver
+ cases: aSendNode args first
+ otherwiseOrNil: (aSendNode args at: 2 ifAbsent: [nil]).
+ (aSendNode receiver isVariable or: [parentNode isStmtList]) ifFalse:
+ [switch switchVariable: (locals add: (self extraVariableName: 'switch'))].
+ ^switch!

Item was changed:
+ ----- Method: TMethod>>endsWithReturn (in category 'testing') -----
- ----- Method: TMethod>>endsWithReturn (in category 'inlining support') -----
  endsWithReturn
  "Answer true if the last statement of this method is a return."
 
+ ^parseTree endsWithReturn!
- ^ parseTree statements last isReturn!

Item was added:
+ ----- Method: TMethod>>isStructAccessor (in category 'testing') -----
+ isStructAccessor
+ ^[definingClass isAccessor: selector]
+ on: MessageNotUnderstood
+ do: [:ex| false]!

Item was changed:
  ----- Method: TMethod>>prepareMethodIn: (in category 'transformations') -----
  prepareMethodIn: aCodeGen
  "Record sends of builtin operators, map sends of the special selector dispatchOn:in:
  with case statement nodes, and map sends of caseOf:[otherwise:] to switch statements.
- Note: Only replaces top-level sends of dispatchOn:in: et al and caseOf:[otherwise:].
- These must be top-level statements; they cannot appear in expressions.
  As a hack also update the types of variables introduced to implement cascades correctly.
+ This has to be done at the same time as this is done, so why not piggy back here?"
+ extraVariableNumber ifNotNil:
- This has to be done at teh same time as this is done, so why not piggy back here?"
- | replacements |.
- cascadeVariableNumber ifNotNil:
  [declarations keysAndValuesDo:
  [:varName :decl|
  decl isBlock ifTrue:
  [self assert: ((varName beginsWith: 'cascade') and: [varName last isDigit]).
  locals add: varName.
  self declarationAt: varName
  put: (decl value: self value: aCodeGen), ' ', varName]]].
- replacements := IdentityDictionary new.
  aCodeGen
  pushScope: declarations
+ while:"N.B.  nodesWithParentsDo: is bottom-up, hence replacement is destructive and conserved."
+ [parseTree nodesWithParentsDo:
+ [:node :parent|
- while:
- [parseTree nodesDo:
- [:node|
  node isSend ifTrue:
+ [(aCodeGen isBuiltinSelector: node selector)
- [(aCodeGen builtin: node selector)
  ifTrue:
  [node isBuiltinOperator: true.
  "If a to:by:do:'s limit has side-effects, declare the limit variable, otherwise delete it from the args"
  (node selector = #to:by:do:
   and: [node args size = 4]) ifTrue:
  [| limitExpr |
  limitExpr := node args first.
  (limitExpr anySatisfy:
  [:subNode|
  subNode isSend
+ and: [(aCodeGen isBuiltinSelector: subNode selector) not
+ and: [(subNode isStructSendIn: aCodeGen) not]]])
- and: [(aCodeGen builtin: subNode selector) not
- and: [(subNode isStructSend: aCodeGen) not]]])
  ifTrue: [ | limitVar |
  limitVar := node args last name.
  "n.b. Two loops in the same method may share the same variable
  for loop limit, so add the variable declaration only if not already
  declared by a previous loop. Assumes that the name of the loop
  limit variable (e.g. 'iLimiT') is unlikely to have been used as an actual
  instance variable elsewhere."
  (locals includes: limitVar) ifFalse: [locals add: limitVar]]
  ifFalse:
  [node arguments: node args allButLast]]]
  ifFalse:
  [(CaseStatements includes: node selector) ifTrue:
+ [parent replaceNodesIn: (Dictionary newFromPairs: { node. self buildCaseStmt: node})].
- [replacements at: node put: (self buildCaseStmt: node)].
  (#(caseOf: #caseOf:otherwise:) includes: node selector) ifTrue:
+ [parent replaceNodesIn: (Dictionary newFromPairs: { node. self buildSwitchStmt: node parent: parent })]]]]]!
- [replacements at: node put: (self buildSwitchStmt: node)]]].
- ((node isAssignment or: [node isReturn])
-  and: [node expression isSwitch]) ifTrue:
- [replacements at: node put: (self transformSwitchExpression: node)]]].
- replacements isEmpty ifFalse:
- [parseTree := parseTree replaceNodesIn: replacements]!

Item was added:
+ ----- Method: TParseNode>>endsWithReturn (in category 'testing') -----
+ endsWithReturn
+
+ ^false!

Item was added:
+ ----- Method: TParseNode>>nodesDo:parent: (in category 'enumerating') -----
+ nodesDo: aBlock parent: parent
+ "Apply aBlock to all nodes in the receiver with each node's parent.
+ N.B. This is assumed to be bottom-up, leaves first."
+ self subclassResponsibility!

Item was added:
+ ----- Method: TParseNode>>nodesWithParentsDo: (in category 'enumerating') -----
+ nodesWithParentsDo: aBlock
+ "Apply aBlock to all nodes in the receiver with each node's parent.
+ N.B. This is assumed to be bottom-up, leaves first."
+ self nodesDo: aBlock parent: nil!

Item was added:
+ ----- Method: TParseNode>>structTargetKindIn: (in category 'testing') -----
+ structTargetKindIn: aCodeGen
+ "Answer if the recever evaluates to a struct or struct pointer
+ and hence can be dereferenced using . or ->.  Answer any of
+ #struct #pointer or nil"
+ ^nil!

Item was added:
+ ----- Method: TReturnNode>>endsWithReturn (in category 'testing') -----
+ endsWithReturn
+
+ ^true!

Item was added:
+ ----- Method: TReturnNode>>nodesDo:parent: (in category 'enumerating') -----
+ nodesDo: aBlock parent: parent
+ "Apply aBlock to all nodes in the receiver with each node's parent.
+ N.B. This is assumed to be bottom-up, leaves first."
+ expression nodesDo: aBlock parent: self.
+ aBlock value: self value: parent!

Item was added:
+ ----- Method: TSendNode>>endsWithReturn (in category 'testing') -----
+ endsWithReturn
+ ^self isReturningIf!

Item was added:
+ ----- Method: TSendNode>>isStructSendIn: (in category 'testing') -----
+ isStructSendIn: aCodeGen
+ "Answer if the recever is a send of a structure accessor.
+ This is tricky.  We want
+ foo bar => foo->bar
+ foo bar => foo.bar
+ foo bar: expr => foo->bar = expr
+ foo bar: expr => foo.bar = expr
+ depending on whether foo is a struct or a pointer to a struct,
+ but only if both foo is a struct type and bar is a field accessor.
+ The tricky cases are self-sends within struct class methods.  Here we need to
+ distinguish between self-sends of ordinary methods from self sends of accessors."
+ ^arguments size <= 1
+   and: [(receiver structTargetKindIn: aCodeGen) notNil
+   and: [(aCodeGen methodNamed: selector)
+ ifNil: [false]
+ ifNotNil: [:method| method isStructAccessor]]]!

Item was added:
+ ----- Method: TSendNode>>nodesDo:parent: (in category 'enumerating') -----
+ nodesDo: aBlock parent: parent
+ "Apply aBlock to all nodes in the receiver with each node's parent.
+ N.B. This is assumed to be bottom-up, leaves first."
+ receiver nodesDo: aBlock parent: self.
+ arguments do: [:arg| arg nodesDo: aBlock parent: self].
+ aBlock value: self value: parent!

Item was added:
+ ----- Method: TSendNode>>structTargetKindIn: (in category 'testing') -----
+ structTargetKindIn: aCodeGen
+ "Answer if the recever evaluates to a struct or struct pointer
+ and hence can be dereferenced using . or ->.  Answer any of
+ #struct #pointer or nil.  Right now we don't need or support
+ structure return so this method answers either #pointer or nil."
+ selector == #cCoerceSimple:to: ifTrue:
+ [^(VMStructType isTypePointerToStruct: arguments last value) ifTrue:
+ [#pointer]].
+
+ selector == #addressOf: ifTrue:
+ [^#pointer].
+
+ selector == #at: ifTrue:
+ [receiver isVariable ifTrue:
+ [(aCodeGen typeOfVariable: receiver name) ifNotNil:
+ [:type| | derefType |
+ type last = $* ifFalse:
+ [^receiver structTargetKindIn: aCodeGen].
+ (VMStructType isTypeStruct: (aCodeGen
+ extractTypeFor: receiver name
+ fromDeclaration: type allButLast)) ifTrue:
+ [^#struct]]].
+ (receiver structTargetKindIn: aCodeGen) ifNotNil:
+ [:kind| ^kind]].
+
+ (aCodeGen selectorReturnsPointerToStruct: selector) ifTrue:
+ [^#pointer].
+
+ (aCodeGen selectorReturnsStruct: selector) ifTrue:
+ [^#struct].
+
+ ^nil!

Item was added:
+ ----- Method: TStmtListNode>>endsWithReturn (in category 'testing') -----
+ endsWithReturn
+ "Answer true if the last statement of this lock is a return."
+
+ ^statements last isReturn or: [statements last isReturningIf]!

Item was added:
+ ----- Method: TStmtListNode>>nodesDo:parent: (in category 'enumerating') -----
+ nodesDo: aBlock parent: parent
+ "Apply aBlock to all nodes in the receiver with each node's parent.
+ N.B. This is assumed to be bottom-up, leaves first."
+ statements do: [:s| s nodesDo: aBlock parent: self].
+ aBlock value: self value: parent!

Item was added:
+ TParseNode subclass: #TSwitchStmtNode
+ instanceVariableNames: 'expression cases otherwiseOrNil switchVariable'
+ classVariableNames: ''
+ poolDictionaries: ''
+ category: 'VMMaker-Translation to C'!
+
+ !TSwitchStmtNode commentStamp: '<historical>' prior: 0!
+ I implement a Smalltalk
+ foo caseOf: { [IntegerConstant | GlobalVariable] -> [expr] }
+ statement converting it into a C switch statement.  I make some effort to discover identical right-hand-side cases.!

Item was added:
+ ----- Method: TSwitchStmtNode>>bindVariableUsesIn: (in category 'transformations') -----
+ bindVariableUsesIn: aDictionary
+ expression := expression bindVariableUsesIn: aDictionary.
+ cases := (cases collect:
+ [:pair| "<Array with: <Array with: ParseTreeNode> with: TStmtListNode"
+ { pair first collect: [:labelNode| labelNode bindVariableUsesIn: aDictionary].
+   pair last bindVariableUsesIn: aDictionary }]).
+ otherwiseOrNil ifNotNil:
+ [otherwiseOrNil := otherwiseOrNil bindVariableUsesIn: aDictionary]!

Item was added:
+ ----- Method: TSwitchStmtNode>>bindVariableUsesIn:andConstantFoldIf:in: (in category 'transformations') -----
+ bindVariableUsesIn: aDictionary andConstantFoldIf: constantFold in: codeGen
+ "Answer either the receiver, if it contains no references to the given variables, or a new node with the given variables rebound."
+ | newExpression newCases newOtherwise |
+ newExpression := expression bindVariableUsesIn: aDictionary andConstantFoldIf: constantFold in: codeGen.
+ newCases := cases collect:
+ [:pair| "<Array with: <Array with: ParseTreeNode> with: TStmtListNode"
+ { pair first collect: [:labelNode| labelNode bindVariableUsesIn: aDictionary andConstantFoldIf: constantFold in: codeGen].
+   pair last bindVariableUsesIn: aDictionary andConstantFoldIf: constantFold in: codeGen}].
+ newOtherwise := otherwiseOrNil ifNotNil:
+ [otherwiseOrNil bindVariableUsesIn: aDictionary andConstantFoldIf: constantFold in: codeGen].
+ ^(newExpression = expression
+   and: [newCases = cases
+   and: [newOtherwise = otherwiseOrNil]])
+ ifTrue: [self]
+ ifFalse:
+ [self shallowCopy
+ expression: newExpression;
+ cases: newCases;
+ otherwiseOrNil: newOtherwise;
+ yourself]!

Item was added:
+ ----- Method: TSwitchStmtNode>>bindVariablesIn: (in category 'transformations') -----
+ bindVariablesIn: aDictionary
+ expression := expression bindVariablesIn: aDictionary.
+ cases := (cases collect:
+ [:pair| "<Array with: <Array with: ParseTreeNode> with: TStmtListNode"
+ { pair first collect: [:labelNode| labelNode bindVariablesIn: aDictionary].
+   pair last bindVariablesIn: aDictionary }]).
+ otherwiseOrNil ifNotNil:
+ [otherwiseOrNil := otherwiseOrNil bindVariablesIn: aDictionary]!

Item was added:
+ ----- Method: TSwitchStmtNode>>cases (in category 'accessing') -----
+ cases
+ "Answer the value of cases"
+
+ ^ cases!

Item was added:
+ ----- Method: TSwitchStmtNode>>cases: (in category 'accessing') -----
+ cases: anObject
+ "Set the value of cases"
+
+ cases := anObject!

Item was added:
+ ----- Method: TSwitchStmtNode>>copyTree (in category 'copying') -----
+ copyTree
+
+ self flag: #FIXME. "adopt from oscog - get rid of copyTree,  use postCopy instead"
+ ^self copy
+ !

Item was added:
+ ----- Method: TSwitchStmtNode>>createCasesFromBraceNode: (in category 'instance initialization') -----
+ createCasesFromBraceNode: aTBraceNode
+ | casesToStrings stringsToLabels newCases |
+ casesToStrings := Dictionary new.
+ stringsToLabels := Dictionary new.
+ newCases := OrderedCollection new: aTBraceNode caseLabels size.
+ aTBraceNode caseLabels with: aTBraceNode cases do:
+ [:label :case| | printString |
+ printString := casesToStrings at: case put: case printString.
+ (stringsToLabels at: printString ifAbsentPut: [OrderedCollection new]) addLast: label].
+
+ aTBraceNode caseLabels with: aTBraceNode cases do:
+ [:label :case| | printString labels |
+ printString := casesToStrings at: case.
+ label = (labels := (stringsToLabels at: printString) asArray) first ifTrue:
+ [newCases addLast: { labels collect: [:ea| ea statements first]. case}]].
+
+ ^newCases!

Item was added:
+ ----- Method: TSwitchStmtNode>>emitCCodeAsExpressionOn:level:generator: (in category 'C code generation') -----
+ emitCCodeAsExpressionOn: aStream level: level generator: aCodeGen
+ "Emit the receiver as an if-the-else chain."
+ | varName n |
+ self assert: (expression isVariable or: [switchVariable notNil]).
+ aStream nextPut: $(.
+ switchVariable
+ ifNil: [varName := String streamContents: [:s| expression emitCCodeOn: s level: 0 generator: aCodeGen].
+ aStream nextPutAll: varName]
+ ifNotNil:
+ [varName := switchVariable.
+ aStream nextPut: $(; nextPutAll: varName; nextPutAll: ' = '.
+ expression emitCCodeAsArgumentOn: aStream level: level generator: aCodeGen.
+ aStream nextPut: $)].
+ n := 0.
+ cases do:
+ [:tuple|
+ [:labels :case|
+  labels do:
+ [:label|
+ n > 0 ifTrue:
+ [aStream nextPutAll: varName].
+ aStream nextPutAll: ' == '.
+ label emitCCodeAsArgumentOn: aStream level: level + 1 generator: aCodeGen.
+ aStream nextPut: $).
+ aStream crtab: level + n + 1.
+ aStream nextPutAll: '? ('.
+ (TStmtListNode new setArguments: #() statements: case statements)
+ emitCCodeAsArgumentOn: aStream
+ level: level + 2
+ generator: aCodeGen.
+ aStream nextPut: $); crtab: level + n + 1; nextPutAll: ': ('.
+ n := n + 1]]
+ valueWithArguments: tuple].
+ otherwiseOrNil
+ ifNotNil: [otherwiseOrNil emitCCodeAsArgumentOn: aStream level: level + 1 generator: aCodeGen]
+ ifNil: [aStream nextPutAll: 'error("Case not found and no otherwise clause"), 0'].
+ aStream next: n - 1 put: $)!

Item was added:
+ ----- Method: TSwitchStmtNode>>emitCCodeOn:addToEndOfCases:level:generator: (in category 'C code generation') -----
+ emitCCodeOn: aStream addToEndOfCases: aNodeOrNil level: level generator: aCodeGen
+
+ aStream crtab: level.
+ aStream nextPutAll: 'switch ('.
+ expression emitCCodeAsArgumentOn: aStream level: level generator: aCodeGen.
+ aStream nextPutAll: ') {'.
+ cases do:
+ [:tuple|
+ [:labels :case|
+  labels do:
+ [:label|
+ aStream
+ crtab: level;
+ nextPutAll: 'case '.
+ label emitCCodeAsArgumentOn: aStream level: level + 1 generator: aCodeGen.
+ aStream nextPut: $:].
+  aStream crtab: level + 1.
+  case emitCCodeOn: aStream prependToEnd: aNodeOrNil level: level + 1 generator: aCodeGen]
+ valueWithArguments: tuple.
+  (aNodeOrNil notNil and: [aNodeOrNil isReturn]) ifFalse:
+ [aStream crtab: level + 1; nextPutAll: 'break;']].
+ aStream
+ crtab: level;
+ nextPutAll: 'default:';
+ crtab: level + 1.
+ otherwiseOrNil
+ ifNotNil: [otherwiseOrNil emitCCodeOn: aStream prependToEnd: aNodeOrNil level: level + 1 generator: aCodeGen]
+ ifNil: [aStream nextPutAll: 'error("Case not found and no otherwise clause");'.
+   aNodeOrNil ifNotNil:
+ [aStream crtab: level + 1.
+ (aNodeOrNil copy setExpression: (TConstantNode new setValue: -1))
+ emitCCodeOn: aStream level: level generator: aCodeGen.
+ aStream nextPut: $;]].
+ aStream
+ crtab: level;
+ nextPut: $}!

Item was added:
+ ----- Method: TSwitchStmtNode>>emitCCodeOn:level:generator: (in category 'C code generation') -----
+ emitCCodeOn: aStream level: level generator: aCodeGen
+
+ aStream crtab: level.
+ aStream nextPutAll: 'switch ('.
+ expression emitCCodeAsArgumentOn: aStream level: level generator: aCodeGen.
+ aStream nextPutAll: ') {'.
+ cases do:
+ [:tuple|
+ [:labels :case|
+  labels do:
+ [:label|
+ aStream
+ crtab: level;
+ nextPutAll: 'case '.
+ label emitCCodeAsArgumentOn: aStream level: level + 1 generator: aCodeGen.
+ aStream nextPut: $:].
+  aStream crtab: level + 1.
+  case emitCCodeOn: aStream level: level + 1 generator: aCodeGen.
+  case endsWithReturn ifFalse:
+ [aStream tab: level + 1; nextPutAll: 'break;']]
+ valueWithArguments: tuple].
+ aStream
+ crtab: level;
+ nextPutAll: 'default:';
+ crtab: level + 1.
+ otherwiseOrNil
+ ifNotNil:
+ [otherwiseOrNil emitCCodeOn: aStream level: level + 1 generator: aCodeGen]
+ ifNil: [aStream nextPutAll: 'error("Case not found and no otherwise clause");'].
+ aStream
+ crtab: level;
+ nextPut: $}!

Item was added:
+ ----- Method: TSwitchStmtNode>>expression (in category 'accessing') -----
+ expression
+ "Answer the value of expression"
+
+ ^ expression!

Item was added:
+ ----- Method: TSwitchStmtNode>>expression: (in category 'accessing') -----
+ expression: anObject
+ "Set the value of expression"
+
+ expression := anObject!

Item was added:
+ ----- Method: TSwitchStmtNode>>expression:cases:otherwiseOrNil: (in category 'instance initialization') -----
+ expression: expr cases: aTBraceNode otherwiseOrNil: otherwiseOrNilNode
+
+ self expression: expr.
+ self cases: (self createCasesFromBraceNode: aTBraceNode).
+ self otherwiseOrNil: otherwiseOrNilNode!

Item was added:
+ ----- Method: TSwitchStmtNode>>isSwitch (in category 'testing') -----
+ isSwitch
+ ^true!

Item was added:
+ ----- Method: TSwitchStmtNode>>nodesDo: (in category 'enumerating') -----
+ nodesDo: aBlock
+ "Apply aBlock to all nodes in the receiver.
+ N.B. This is assumed to be bottom-up, leaves first."
+ expression nodesDo: aBlock.
+ cases do:
+ [:pair|
+ pair first do: [:node| node nodesDo: aBlock].
+ pair last nodesDo: aBlock].
+ otherwiseOrNil ifNotNil:
+ [otherwiseOrNil nodesDo: aBlock].
+ aBlock value: self!

Item was added:
+ ----- Method: TSwitchStmtNode>>nodesDo:parent: (in category 'enumerating') -----
+ nodesDo: aBlock parent: parent
+ "Apply aBlock to all nodes in the receiver with each node's parent.
+ N.B. This is assumed to be bottom-up, leaves first."
+ expression nodesDo: aBlock parent: self..
+ cases do:
+ [:pair|
+ pair first do: [:node| node nodesDo: aBlock parent: self.].
+ pair last nodesDo: aBlock parent: self.].
+ otherwiseOrNil ifNotNil:
+ [otherwiseOrNil nodesDo: aBlock parent: self].
+ aBlock value: self value: parent!

Item was added:
+ ----- Method: TSwitchStmtNode>>nodesDo:unless: (in category 'enumerating') -----
+ nodesDo: aBlock unless: cautionaryBlock
+
+ (cautionaryBlock value: self) ifTrue: [^self].
+ expression nodesDo: aBlock unless: cautionaryBlock.
+ cases do:
+ [:pair|
+ pair first do: [:node| node nodesDo: aBlock unless: cautionaryBlock].
+ pair last nodesDo: aBlock unless: cautionaryBlock].
+ otherwiseOrNil ifNotNil:
+ [otherwiseOrNil nodesDo: aBlock unless: cautionaryBlock]!

Item was added:
+ ----- Method: TSwitchStmtNode>>otherwiseOrNil (in category 'accessing') -----
+ otherwiseOrNil
+ "Answer the value of otherwiseOrNil"
+
+ ^ otherwiseOrNil!

Item was added:
+ ----- Method: TSwitchStmtNode>>otherwiseOrNil: (in category 'accessing') -----
+ otherwiseOrNil: anObject
+ "Set the value of otherwiseOrNil"
+
+ otherwiseOrNil := anObject!

Item was added:
+ ----- Method: TSwitchStmtNode>>postCopy (in category 'copying') -----
+ postCopy
+ expression := expression copy.
+ cases := (cases collect:
+ [:pair| "<Array with: <Array with: ParseTreeNode> with: TStmtListNode"
+ { pair first collect: [:labelNode| labelNode copy].
+   pair last copy }]).
+ otherwiseOrNil := otherwiseOrNil copy!

Item was added:
+ ----- Method: TSwitchStmtNode>>printOn:level: (in category 'printing') -----
+ printOn: aStream level: level
+
+ aStream crtab: level.
+ aStream nextPutAll: 'switch ('.
+ expression printOn: aStream level: level.
+ aStream nextPutAll: ') {'.
+ cases do:
+ [:tuple|
+ [:labels :case|
+  labels do:
+ [:label|
+ aStream
+ crtab: level;
+ nextPutAll: 'case '.
+ label printOn: aStream level: level + 1.
+ aStream nextPut: $:].
+  aStream crtab: level + 1.
+  case printOn: aStream level: level + 1.
+  aStream crtab: level + 1; nextPutAll: 'break;']
+ valueWithArguments: tuple].
+ otherwiseOrNil ifNotNil:
+ [aStream
+ crtab: level;
+ nextPutAll: 'default:';
+ crtab: level + 1.
+ otherwiseOrNil printOn: aStream level: level + 1].
+ aStream
+ crtab: level;
+ nextPut: $}!

Item was added:
+ ----- Method: TSwitchStmtNode>>removeAssertions (in category 'transformations') -----
+ removeAssertions
+ expression removeAssertions.
+ cases do:
+ [:pair| "<Array with: <Array with: ParseTreeNode> with: TStmtListNode"
+ pair first do: [:labelNode| labelNode removeAssertions].
+ pair last removeAssertions].
+ otherwiseOrNil ifNotNil:
+ [otherwiseOrNil removeAssertions]!

Item was added:
+ ----- Method: TSwitchStmtNode>>replaceNodesIn: (in category 'transformations') -----
+ replaceNodesIn: aDictionary
+ ^aDictionary
+ at: self
+ ifAbsent:
+ [expression := expression replaceNodesIn: aDictionary.
+ cases := (cases collect:
+ [:pair| "<Array with: <Array with: ParseTreeNode> with: TStmtListNode"
+ { pair first collect: [:labelNode| labelNode replaceNodesIn: aDictionary].
+   pair last replaceNodesIn: aDictionary }]).
+ otherwiseOrNil ifNotNil:
+ [otherwiseOrNil := otherwiseOrNil replaceNodesIn: aDictionary].
+ self]!

Item was added:
+ ----- Method: TSwitchStmtNode>>switchVariable (in category 'accessing') -----
+ switchVariable
+ "Answer the value of switchVariable"
+
+ ^ switchVariable!

Item was added:
+ ----- Method: TSwitchStmtNode>>switchVariable: (in category 'accessing') -----
+ switchVariable: anObject
+ "Set the value of switchVariable"
+
+ switchVariable := anObject!

Item was added:
+ ----- Method: TVariableNode>>nodesDo:parent: (in category 'enumerating') -----
+ nodesDo: aBlock parent: parent
+ "Apply aBlock to all nodes in the receiver with each node's parent.
+ N.B. This is assumed to be bottom-up, leaves first."
+ aBlock value: self value: parent!

Item was added:
+ ----- Method: TVariableNode>>structTargetKindIn: (in category 'testing') -----
+ structTargetKindIn: aCodeGen
+ "Answer if the recever evaluates to a struct or struct pointer
+ and hence can be dereferenced using . or ->.  Answer any of
+ #struct #pointer or nil"
+ ^aCodeGen structTargetKindForVariableName: name!

Item was changed:
  ----- Method: VMMaker class>>versionString (in category 'version testing') -----
  versionString
 
  "VMMaker versionString"
 
+ ^'4.12.14'!
- ^'4.12.13'!