Skip to content

Commit

Permalink
Merge pull request #42 from kntkymt/fix/generater-structure
Browse files Browse the repository at this point in the history
Genとアセンブリ出力を分離する
  • Loading branch information
kntkymt authored Feb 3, 2024
2 parents 508c831 + 311c603 commit c1db97a
Show file tree
Hide file tree
Showing 8 changed files with 1,057 additions and 489 deletions.
10 changes: 10 additions & 0 deletions .swiftpm/xcode/xcshareddata/xcschemes/CCompiler.xcscheme
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,16 @@
ReferencedContainer = "container:">
</BuildableReference>
</TestableReference>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "GeneratorTest"
BuildableName = "GeneratorTest"
BlueprintName = "GeneratorTest"
ReferencedContainer = "container:">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
Expand Down
4 changes: 4 additions & 0 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ let package = Package(
name: "CCompilerCoreTest",
dependencies: ["CCompilerCore"]
),
.testTarget(
name: "GeneratorTest",
dependencies: ["Generator"]
),
.testTarget(
name: "ParserTest",
dependencies: ["Parser", "AST"]
Expand Down
2 changes: 1 addition & 1 deletion Sources/AST/ASTGenerator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ public enum ASTGenerator {
}

static func generate(syntax: any SyntaxProtocol) -> any NodeProtocol {
return switch syntax.kind {
switch syntax.kind {
case .integerLiteral: generate(integerLiteral: syntax.casted(IntegerLiteralSyntax.self))
case .declReference: generate(declReference: syntax.casted(DeclReferenceSyntax.self))
case .stringLiteral: generate(stringLiteral: syntax.casted(StringLiteralSyntax.self))
Expand Down
5 changes: 3 additions & 2 deletions Sources/CCompilerCore/CCompilerCore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@ public func compile(_ source: String) throws -> String {
let tokens = try Tokenizer(source: source).tokenize()
let syntax = try Parser(tokens: tokens).parse()
let node = ASTGenerator.generate(sourceFileSyntax: syntax)

return try Generator().generate(sourceFileNode: node)
let asm = try Generator().generate(sourceFileNode: node)

return ArmAsmPrinter.print(instructions: asm)
} catch let error as TokenizeError {
switch error {
case .unknownToken(let location):
Expand Down
128 changes: 128 additions & 0 deletions Sources/Generator/ArmAsmPrinter.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
private extension AsmRepresent.Instruction.Address {
var description: String {
switch self {
case .register(let register): register.rawValue
case .distance(let register, let diff): "\(register.rawValue), #\(diff)"
}
}
}

public enum ArmAsmPrinter {
public static func print(instructions: [AsmRepresent.Instruction]) -> String {
instructions.map { print(instruction: $0) }.reduce("") { $0 + $1 }
}

static func print(instruction: AsmRepresent.Instruction) -> String {
/// opecode a, b
func inst(_ opecode: String, _ a: AsmRepresent.Register, _ b: AsmRepresent.Register) -> String {
" \(opecode) \(a.rawValue), \(b.rawValue)\n"
}

/// opecode a, #immediate
func inst(_ opecode: String, _ a: AsmRepresent.Register, _ immediate: Int64) -> String {
" \(opecode) \(a.rawValue), #\(immediate)\n"
}

/// opecode a, b, c
func inst(_ opecode: String, _ a: AsmRepresent.Register, _ b: AsmRepresent.Register, _ c: AsmRepresent.Register) -> String {
" \(opecode) \(a.rawValue), \(b.rawValue), \(c.rawValue)\n"
}

/// opecode a, b, #immediate
func inst(_ opecode: String, _ a: AsmRepresent.Register, _ b: AsmRepresent.Register, _ immediate: Int64) -> String {
" \(opecode) \(a.rawValue), \(b.rawValue), #\(immediate)\n"
}

/// opecode a, [address]
func inst(_ opecode: String, _ a: AsmRepresent.Register, _ address: AsmRepresent.Instruction.Address) -> String {
" \(opecode) \(a.rawValue), [\(address.description)]\n"
}

/// opecode a, b, [address]
func inst(_ opecode: String, _ a: AsmRepresent.Register, _ b: AsmRepresent.Register, _ address: AsmRepresent.Instruction.Address) -> String {
" \(opecode) \(a.rawValue), \(b.rawValue), [\(address.description)]\n"
}

/// opecode label
func inst(_ opecode: String, _ label: String) -> String {
" \(opecode) \(fixLabel(label))\n"
}

/// AppleClangではmainが_main じゃないとダメなので対応
func fixLabel(_ label: String) -> String {
label == "main" ? "_main" : label
}

return switch instruction {
case .mov(let dst, let src):
inst("mov", dst, src)
case .movi(let dst, let immediate):
inst("mov", dst, immediate)
case .add(let dst, let src1, let src2):
inst("add", dst, src1, src2)
case .addi(let dst, let src, let immediate):
inst("add", dst, src, immediate)
case .sub(let dst, let src1, let src2):
inst("sub", dst, src1, src2)
case .subi(let dst, let src, let immediate):
inst("sub", dst, src, immediate)
case .mul(let dst, let src1, let src2):
inst("mul", dst, src1, src2)
case .muli(let dst, let src, let immediate):
inst("mul", dst, src, immediate)
case .div(let dst, let src1, let src2):
inst("sdiv", dst, src1, src2)
case .divi(let dst, let src, let immediate):
inst("sdiv", dst, src, immediate)
case .push(let src):
inst("sub", .sp, .sp, 16)
+ inst("str", src, .register(.sp))
case .pop(let dst):
inst("ldr", dst, .register(.sp))
+ inst("add", .sp, .sp, 16)
case .addr(let dst, let label):
" adrp \(dst.rawValue), \(label)@GOTPAGE\n"
+ " ldr \(dst.rawValue), [\(dst.rawValue), \(label)@GOTPAGEOFF]\n"
case .str(let src, let address):
inst("str", src, address)
case .strb(let src, let address):
inst("strb", src, address)
case .stp(let src1, let src2, let address):
inst("stp", src1, src2, address)
case .ldr(let dst, let address):
inst("ldr", dst, address)
case .ldrb(let dst, let address):
inst("ldrb", dst, address)
case .ldp(let dst1, let dst2, let address):
inst("ldp", dst1, dst2, address)
case .cmp(let src1, let src2):
inst("cmp", src1, src2)
case .cmpi(let src, let immediate):
inst("cmp", src, immediate)
case .cset(let dst, let flag):
" cset \(dst.rawValue), \(flag.rawValue)\n"
case .beq(let label):
inst("beq", label)
case .b(let label):
inst("b", label)
case .bl(let label):
inst("bl", label)
case .neg(let dst, let src):
inst("neg", dst, src)
case .lsli(let dst, let src, let immediate):
inst("lsl", dst, src, Int64(immediate))
case .ret:
" ret\n"
case .label(let label):
"\(fixLabel(label)):\n"
case .p2align(let factor):
".p2align \(factor)\n"
case .section(let kinds):
".section \(kinds.map { $0.rawValue }.joined(separator: ","))\n"
case .globl(let label):
".globl \(fixLabel(label))\n"
case .dataDecl(let kind, let value):
" .\(kind.rawValue) \(value)\n"
}
}
}
123 changes: 123 additions & 0 deletions Sources/Generator/AsmRepresentation.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@

public enum AsmRepresent {

public enum Instruction {
case mov(dst: Register, src: Register)
case movi(dst: Register, immediate: Int64)

case add(dst: Register, src1: Register, src2: Register)
case addi(dst: Register, src: Register, immediate: Int64)
case sub(dst: Register, src1: Register, src2: Register)
case subi(dst: Register, src: Register, immediate: Int64)
case mul(dst: Register, src1: Register, src2: Register)
case muli(dst: Register, src: Register, immediate: Int64)
case div(dst: Register, src1: Register, src2: Register)
case divi(dst: Register, src: Register, immediate: Int64)

case push(src: Register)
case pop(dst: Register)

/// dstにlabelのアドレスを入れる
case addr(dst: Register, label: String)

case str(src: Register, address: Address)
case strb(src: Register, address: Address)
case stp(src1: Register, src2: Register, address: Address)
case ldr(dst: Register, address: Address)
case ldrb(dst: Register, address: Address)
case ldp(dst1: Register, dst2: Register, address: Address)

case cmp(src1: Register, src2: Register)
case cmpi(src: Register, immediate: Int64)

case cset(dst: Register, flag: CsetFlag)

case beq(label: String)
case b(label: String)
case bl(label: String)

case neg(des: Register, src: Register)

case lsli(dst: Register, src: Register, immediate: UInt64)

case ret

// 実際には命令ではない、別のデータ構造で表すべき?
case label(label: String)
case p2align(factor: UInt64)
case section(kinds: [SectionKind])
case globl(label: String)
case dataDecl(kind: DataKind, value: String)

public enum Address {
case register(_ register: Register)
case distance(_ register: Register, _ diff: Int64)
}

public enum CsetFlag: String {
case eq
case ne
case lt
case le
case gt
case ge
}

public enum SectionKind: String {
case DATA = "__DATA"
case data = "__data"
case TEXT = "__TEXT"
case cstring = "__cstring"
case cstring_literals = "cstring_literals"
}

public enum DataKind: String {
case byte
case quad
case asciz
case comm
}
}

public enum Register: String {
case x0
case x1
case x2
case x3
case x4

case w0
case w1
case w2
case w3
case w4

case x29
case x30

case sp

static func x(_ index: Int) -> Register {
switch index {
case 0: .x0
case 1: .x1
case 2: .x2
case 3: .x3
case 4: .x4
default: fatalError()
}
}

static func w(_ index: Int) -> Register {
switch index {
case 0: .w0
case 1: .w1
case 2: .w2
case 3: .w3
case 4: .w4
default: fatalError()
}
}
}

}
Loading

0 comments on commit c1db97a

Please sign in to comment.