Upgrade to Pro — share decks privately, control downloads, hide ads and more …

How to Clang your Dragon ๐Ÿ‰: Building a Compiler with LLVM

Harlan
February 23, 2017

How to Clang your Dragon ๐Ÿ‰: Building a Compiler withย LLVM

Compilers are treated with a fearful reverence that betrays how fascinating and approachable they really are. In this talk, we'll discuss LLVM and how it works by building a real compiler for a toy language called Kaleidoscope. We'll implement many of the fundamental building blocks of compilers and see how certain Swift features make it a great compiler language.

Harlan

February 23, 2017
Tweet

More Decks by Harlan

Other Decks in Programming

Transcript

  1. def fact(n) if n = 0 then 1 else n

    * fact(n - 1); [ ] .def, .ident("fact"), .semicolon .ident("n"), .if, .ident("n"), .oper(=), .num(0), .then, .num(1), .else, .oper(*), .ident("fact"), .lParen, .ident("n"), .oper(-), .num(1), .rParen, .lParen, .rParen, .ident("n"),
  2. enum Token { } case leftParen, rightParen, def, extern, comma,

    semicolon, `if`, then, `else` case identifier(String) case number(Double)
  3. enum Token { } case leftParen, rightParen, def, extern, comma,

    semicolon, `if`, then, `else` case identifier(String) case number(Double) case `operator`(BinaryOperator) enum BinaryOperator: Character { case plus = "+", minus = "-", times = "*", divide = "/", mod = "%", equals = "=" }
  4. class Lexer { init(input: String) { self.input = input self.index

    = input.startIndex } var currentChar: Character? { return index < input.endIndex ? input[index] : nil } func advanceIndex() { input.characters.formIndex(after: &index) } let input: String var index: String.Index
  5. func advanceToNextToken() -> Token? { while let char = currentChar,

    char.isSpace { advanceIndex() } guard let char = currentChar else { return nil }
  6. func advanceToNextToken() -> Token? { while let char = currentChar,

    char.isSpace { advanceIndex() } guard let char = currentChar else { return nil } let singleTokMapping: [Character: Token] = [ ",": .comma, "(": .leftParen, ")": .rightParen, ";": .semicolon, "+": .operator(.plus), "-": .operator(.minus), "*": .operator(.times), "/": .operator(.divide), "%": .operator(.mod), "=": .operator(.equals) ]
  7. func advanceToNextToken() -> Token? { while let char = currentChar,

    char.isSpace { advanceIndex() } guard let char = currentChar else { return nil } if let tok = singleTokMapping[char] { advanceIndex() return tok } let singleTokMapping: [Character: Token] = [ ",": .comma, "(": .leftParen, ")": .rightParen, ";": .semicolon, "+": .operator(.plus), "-": .operator(.minus), "*": .operator(.times), "/": .operator(.divide), "%": .operator(.mod), "=": .operator(.equals) ]
  8. if char.isAlphanumeric { let str = readIdentifierOrNumber() if let dblVal

    = Double(str) { return .number(dblVal) } switch str { case "def": return .def case "extern": return .extern case "if": return .if case "then": return .then case "else": return .else default: return .identifier(str) }
  9. if char.isAlphanumeric { let str = readIdentifierOrNumber() if let dblVal

    = Double(str) { return .number(dblVal) } switch str { case "def": return .def case "extern": return .extern case "if": return .if case "then": return .then case "else": return .else default: return .identifier(str) } } return nil }
  10. func lex() -> [Token] { var toks = [Token]() while

    let tok = advanceToNextToken() { toks.append(tok) } return toks } }
  11. extern sin(n); &YUFSOBM%FGJOJUJPOT def plusOne(n) n + 1; 'VODUJPO%FGJOJUJPOT struct

    Prototype { let name: String let params: [String] } struct Definition { let prototype: Prototype let expr: Expr } 4XJGU4USVDUVSF ,BMFJEPTDPQF"45
  12. extern sin(n); &YUFSOBM%FGJOJUJPOT def plusOne(n) n + 1; 'VODUJPO%FGJOJUJPOT n

    + 1 * foo(n * 3); &YQSFTTJPOT struct Prototype { let name: String let params: [String] } 4XJGU4USVDUVSF struct Definition { let prototype: Prototype let expr: Expr } indirect enum Expr { case number(Double) case variable(String) case binary(Expr, BinaryOperator, Expr) case call(String, [Expr]) case ifelse(Expr, Expr, Expr) } ,BMFJEPTDPQF"45
  13. var externs = [Prototype]() var definitions = [Definition]() var expressions

    = [Expr]() var prototypeMap = [String: Prototype]() class File { func prototype(name: String) -> Prototype? { return prototypeMap[name] }
  14. var externs = [Prototype]() var definitions = [Definition]() var expressions

    = [Expr]() var prototypeMap = [String: Prototype]() func addExtern(_ prototype: Prototype) { externs.append(prototype) prototypeMap[prototype.name] = prototype } func addDefinition(_ definition: Definition) { definitions.append(definition) prototypeMap[definition.prototype.name] = definition.prototype } class File { func prototype(name: String) -> Prototype? { return prototypeMap[name] }
  15. var externs = [Prototype]() var definitions = [Definition]() var expressions

    = [Expr]() var prototypeMap = [String: Prototype]() class File { func addExpression(_ expression: Expr) { expressions.append(expression) } func addExtern(_ prototype: Prototype) { externs.append(prototype) prototypeMap[prototype.name] = prototype } func addDefinition(_ definition: Definition) { definitions.append(definition) prototypeMap[definition.prototype.name] = definition.prototype } func prototype(name: String) -> Prototype? { return prototypeMap[name] } }
  16. class Parser { enum ParseError: Error { case unexpectedToken(Token) case

    unexpectedEOF } let tokens: [Token] var index = 0 init(tokens: [Token]) { self.tokens = tokens } var currentToken: Token? { return index < tokens.count ? tokens[index] : nil } func consumeToken() { index += 1 }
  17. func consume(_ token: Token) throws { guard let tok =

    currentToken else { throw ParseError.unexpectedEOF } guard token == tok else { throw ParseError.unexpectedToken(token) } consumeToken() }
  18. func consume(_ token: Token) throws { guard let tok =

    currentToken else { throw ParseError.unexpectedEOF } guard token == tok else { throw ParseError.unexpectedToken(token) } consumeToken() } func parsePrototype() throws -> Prototype { let name = try parseIdentifier() let params = try parseCommaSeparatedInParens(parseIdentifier) return Prototype(name: name, params: params) }
  19. func consume(_ token: Token) throws { guard let tok =

    currentToken else { throw ParseError.unexpectedEOF } guard token == tok else { throw ParseError.unexpectedToken(token) } consumeToken() } func parsePrototype() throws -> Prototype { let name = try parseIdentifier() let params = try parseCommaSeparatedInParens(parseIdentifier) return Prototype(name: name, params: params) } func parseExtern() throws -> Prototype { try consume(.extern) let prototype = try parsePrototype() try consume(.semicolon) return prototype }
  20. func parseExpr() throws -> Expr { guard let token =

    currentToken else { throw ParseError.unexpectedEOF }
  21. func parseExpr() throws -> Expr { guard let token =

    currentToken else { throw ParseError.unexpectedEOF } var expr: Expr switch token {
  22. func parseExpr() throws -> Expr { guard let token =

    currentToken else { throw ParseError.unexpectedEOF } var expr: Expr case .number(let value): consumeToken() expr = .number(value) switch token {
  23. func parseExpr() throws -> Expr { guard let token =

    currentToken else { throw ParseError.unexpectedEOF } var expr: Expr case .leftParen: // ( <expr> ) consumeToken() expr = try parseExpr() try consume(.rightParen) case .number(let value): consumeToken() expr = .number(value) switch token {
  24. func parseExpr() throws -> Expr { guard let token =

    currentToken else { throw ParseError.unexpectedEOF } var expr: Expr case .leftParen: // ( <expr> ) consumeToken() expr = try parseExpr() try consume(.rightParen) case .number(let value): consumeToken() expr = .number(value) case .identifier(let value): consumeToken() if case .leftParen? = currentToken { let params = try parseCommaSeparatedInParens(parseExpr) expr = .call(value, params) } else { expr = .variable(value) } switch token {
  25. case .identifier(let value): consumeToken() if case .leftParen? = currentToken {

    let params = try parseCommaSeparatedInParens(parseExpr) expr = .call(value, params) } else { expr = .variable(value) } case .if: // if <expr> then <expr> else <expr> consumeToken() let cond = try parseExpr() try consume(.then) let thenVal = try parseExpr() try consume(.else) let elseVal = try parseExpr() expr = .ifelse(cond, thenVal, elseVal)
  26. case .identifier(let value): consumeToken() if case .leftParen? = currentToken {

    let params = try parseCommaSeparatedInParens(parseExpr) expr = .call(value, params) } else { expr = .variable(value) } case .if: // if <expr> then <expr> else <expr> consumeToken() let cond = try parseExpr() try consume(.then) let thenVal = try parseExpr() try consume(.else) let elseVal = try parseExpr() expr = .ifelse(cond, thenVal, elseVal) default: throw ParseError.unexpectedToken(token) }
  27. case .if: // if <expr> then <expr> else <expr> consumeToken()

    let cond = try parseExpr() try consume(.then) let thenVal = try parseExpr() try consume(.else) let elseVal = try parseExpr() expr = .ifelse(cond, thenVal, elseVal) default: throw ParseError.unexpectedToken(token) } if case .operator(let op)? = currentToken { consumeToken() let rhs = try parseExpr() return .binary(expr, op, rhs) } return expr }
  28. func parseDefinition() throws -> Definition { try consume(.def) let prototype

    = try parsePrototype() let expr = try parseExpr() try consume(.semicolon) return Definition(prototype: prototype, expr: expr) }
  29. func parseFile() throws -> File { let file = File()

    while let tok = currentToken { switch tok { case .extern: file.addExtern(try parseExtern()) case .def: file.addDefinition(try parseDefinition()) default: let expr = try parseExpr() try consume(.semicolon) file.addExpression(expr) } } return file }
  30. --7.*3  )JHIMFWFM"TTFNCMZ  /PMPPQT POMZHPUPT  &YQMJDJUUZQFTFWFSZXIFSF  %POUUIJOLBCPVUCJUT

     4UBUJD4JOHMF"TTJHONFOU define double @plusOne(double %n) { entry: %0 = fadd double %n, 1.000000e+00 ret double %0 } def plusOne(n) n + 1;
  31. 4UBUJD4JOHMF"TTJHONFOU define double @plusOne(double %n) { entry: %0 = fadd

    double %n, 1.000000e+00 %1 = fmul double %0, 2.000000e+00
  32. 4UBUJD4JOHMF"TTJHONFOU define double @plusOne(double %n) { entry: %0 = fadd

    double %n, 1.000000e+00 %1 = fmul double %0, 2.000000e+00 %2 = fdiv double %1, 3.141592e+00
  33. 4UBUJD4JOHMF"TTJHONFOU define double @plusOne(double %n) { entry: %0 = fadd

    double %n, 1.000000e+00 %1 = fmul double %0, 2.000000e+00 %2 = fdiv double %1, 3.141592e+00 ret double %2 }
  34. class IRGenerator { private var parameterValues = [String: IRValue]() let

    module: Module let builder: IRBuilder let file: File
  35. class IRGenerator { private var parameterValues = [String: IRValue]() init(file:

    File) { self.module = Module(name: "kaleidoscope") self.builder = IRBuilder(module: module) self.file = file } let module: Module let builder: IRBuilder let file: File
  36. @discardableResult func emitPrototype(_ prototype: Prototype) -> Function { if let

    function = module.function(named: prototype.name) { return function }
  37. @discardableResult func emitPrototype(_ prototype: Prototype) -> Function { if let

    function = module.function(named: prototype.name) { return function } let argTypes = [IRType](repeating: FloatType.double, count: prototype.params.count)
  38. @discardableResult func emitPrototype(_ prototype: Prototype) -> Function { if let

    function = module.function(named: prototype.name) { return function } let argTypes = [IRType](repeating: FloatType.double, count: prototype.params.count) let funcType = FunctionType(argTypes: argTypes, returnType: FloatType.double)
  39. @discardableResult func emitPrototype(_ prototype: Prototype) -> Function { if let

    function = module.function(named: prototype.name) { return function } let argTypes = [IRType](repeating: FloatType.double, count: prototype.params.count) let funcType = FunctionType(argTypes: argTypes, returnType: FloatType.double) let function = builder.addFunction(prototype.name, type: funcType)
  40. @discardableResult func emitPrototype(_ prototype: Prototype) -> Function { if let

    function = module.function(named: prototype.name) { return function } let argTypes = [IRType](repeating: FloatType.double, count: prototype.params.count) let funcType = FunctionType(argTypes: argTypes, returnType: FloatType.double) let function = builder.addFunction(prototype.name, type: funcType) for (var param, name) in zip(function.parameters, prototype.params) { param.name = name }
  41. @discardableResult func emitPrototype(_ prototype: Prototype) -> Function { if let

    function = module.function(named: prototype.name) { return function } let argTypes = [IRType](repeating: FloatType.double, count: prototype.params.count) let funcType = FunctionType(argTypes: argTypes, returnType: FloatType.double) let function = builder.addFunction(prototype.name, type: funcType) for (var param, name) in zip(function.parameters, prototype.params) { param.name = name } return function }
  42. @discardableResult func emitPrototype(_ prototype: Prototype) -> Function { if let

    function = module.function(named: prototype.name) { return function } let argTypes = [IRType](repeating: FloatType.double, count: prototype.params.count) let funcType = FunctionType(argTypes: argTypes, returnType: FloatType.double) let function = builder.addFunction(prototype.name, type: funcType) for (var param, name) in zip(function.parameters, prototype.params) { param.name = name } return function } // declare double @foo(double %n, double %m)
  43. @discardableResult func emitDefinition(_ definition: Definition) throws -> Function { let

    function = emitPrototype(definition.prototype) for (idx, arg) in definition.prototype.params.enumerated() { let param = function.parameter(at: idx)! parameterValues[arg] = param }
  44. @discardableResult func emitDefinition(_ definition: Definition) throws -> Function { let

    function = emitPrototype(definition.prototype) for (idx, arg) in definition.prototype.params.enumerated() { let param = function.parameter(at: idx)! parameterValues[arg] = param } let entryBlock = function.appendBasicBlock(named: "entry") builder.positionAtEnd(of: entryBlock)
  45. #BTJD#MPDLT define double @plusOne(double %n) { entry: %r = fadd

    double %n, 1.000000e+00 br %next next: %g = fmul double %r, 1.100000e+00 ret double %g }
  46. @discardableResult func emitDefinition(_ definition: Definition) throws -> Function { let

    function = emitPrototype(definition.prototype) for (idx, arg) in definition.prototype.params.enumerated() { let param = function.parameter(at: idx)! parameterValues[arg] = param } let entryBlock = function.appendBasicBlock(named: "entry") builder.positionAtEnd(of: entryBlock)
  47. @discardableResult func emitDefinition(_ definition: Definition) throws -> Function { let

    function = emitPrototype(definition.prototype) for (idx, arg) in definition.prototype.params.enumerated() { let param = function.parameter(at: idx)! parameterValues[arg] = param } let entryBlock = function.appendBasicBlock(named: "entry") builder.positionAtEnd(of: entryBlock) let expr = try emitExpr(definition.expr) builder.buildRet(expr)
  48. @discardableResult func emitDefinition(_ definition: Definition) throws -> Function { let

    function = emitPrototype(definition.prototype) for (idx, arg) in definition.prototype.params.enumerated() { let param = function.parameter(at: idx)! parameterValues[arg] = param } let entryBlock = function.appendBasicBlock(named: "entry") builder.positionAtEnd(of: entryBlock) let expr = try emitExpr(definition.expr) builder.buildRet(expr) parameterValues.removeAll() return function }
  49. func emitExpr(_ expr: Expr) throws -> IRValue { switch expr

    { case .number(let value): return FloatType.double.constant(value)
  50. func emitExpr(_ expr: Expr) throws -> IRValue { switch expr

    { case .number(let value): return FloatType.double.constant(value) case .variable(let name): guard let param = parameterValues[name] else { throw IRError.unknownVariable(name) } return param
  51. func emitExpr(_ expr: Expr) throws -> IRValue { switch expr

    { case .number(let value): return FloatType.double.constant(value) case .variable(let name): guard let param = parameterValues[name] else { throw IRError.unknownVariable(name) } return param case .call(let name, let args): guard let prototype = file.prototype(name: name) else { throw IRError.unknownFunction(name) }
  52. func emitExpr(_ expr: Expr) throws -> IRValue { switch expr

    { case .number(let value): return FloatType.double.constant(value) case .variable(let name): guard let param = parameterValues[name] else { throw IRError.unknownVariable(name) } return param guard prototype.params.count == args.count else { throw IRError.wrongNumberOfArgs(name, expected: prototype.params.count, got: args.count) } case .call(let name, let args): guard let prototype = file.prototype(name: name) else { throw IRError.unknownFunction(name) }
  53. func emitExpr(_ expr: Expr) throws -> IRValue { switch expr

    { case .number(let value): return FloatType.double.constant(value) case .variable(let name): guard let param = parameterValues[name] else { throw IRError.unknownVariable(name) } return param guard prototype.params.count == args.count else { throw IRError.wrongNumberOfArgs(name, expected: prototype.params.count, got: args.count) } let callArgs = try args.map(emitExpr) case .call(let name, let args): guard let prototype = file.prototype(name: name) else { throw IRError.unknownFunction(name) }
  54. func emitExpr(_ expr: Expr) throws -> IRValue { switch expr

    { case .number(let value): return FloatType.double.constant(value) case .variable(let name): guard let param = parameterValues[name] else { throw IRError.unknownVariable(name) } return param guard prototype.params.count == args.count else { throw IRError.wrongNumberOfArgs(name, expected: prototype.params.count, got: args.count) } let callArgs = try args.map(emitExpr) let function = emitPrototype(prototype) case .call(let name, let args): guard let prototype = file.prototype(name: name) else { throw IRError.unknownFunction(name) } return builder.buildCall(function, args: callArgs)
  55. case .binary(let lhs, let op, let rhs): let lhsVal =

    try emitExpr(lhs) let rhsVal = try emitExpr(rhs)
  56. case .binary(let lhs, let op, let rhs): let lhsVal =

    try emitExpr(lhs) let rhsVal = try emitExpr(rhs) switch op {
  57. case .binary(let lhs, let op, let rhs): let lhsVal =

    try emitExpr(lhs) let rhsVal = try emitExpr(rhs) switch op { case .plus: return builder.buildAdd(lhsVal, rhsVal)
  58. case .binary(let lhs, let op, let rhs): let lhsVal =

    try emitExpr(lhs) let rhsVal = try emitExpr(rhs) switch op { case .plus: return builder.buildAdd(lhsVal, rhsVal) case .minus: return builder.buildSub(lhsVal, rhsVal) case .divide: return builder.buildDiv(lhsVal, rhsVal) case .times: return builder.buildMul(lhsVal, rhsVal) case .mod: return builder.buildRem(lhsVal, rhsVal)
  59. case .binary(let lhs, let op, let rhs): let lhsVal =

    try emitExpr(lhs) let rhsVal = try emitExpr(rhs) switch op { case .plus: return builder.buildAdd(lhsVal, rhsVal) case .minus: return builder.buildSub(lhsVal, rhsVal) case .divide: return builder.buildDiv(lhsVal, rhsVal) case .times: return builder.buildMul(lhsVal, rhsVal) case .mod: return builder.buildRem(lhsVal, rhsVal) case .equals:
  60. case .binary(let lhs, let op, let rhs): let lhsVal =

    try emitExpr(lhs) let rhsVal = try emitExpr(rhs) switch op { case .plus: return builder.buildAdd(lhsVal, rhsVal) case .minus: return builder.buildSub(lhsVal, rhsVal) case .divide: return builder.buildDiv(lhsVal, rhsVal) case .times: return builder.buildMul(lhsVal, rhsVal) case .mod: return builder.buildRem(lhsVal, rhsVal) case .equals: let comparison = builder.buildFCmp(lhsVal, rhsVal, .orderedEqual)
  61. case .binary(let lhs, let op, let rhs): let lhsVal =

    try emitExpr(lhs) let rhsVal = try emitExpr(rhs) switch op { case .plus: return builder.buildAdd(lhsVal, rhsVal) case .minus: return builder.buildSub(lhsVal, rhsVal) case .divide: return builder.buildDiv(lhsVal, rhsVal) case .times: return builder.buildMul(lhsVal, rhsVal) case .mod: return builder.buildRem(lhsVal, rhsVal) case .equals: return builder.buildIntToFP(comparison, type: FloatType.double, signed: false) } let comparison = builder.buildFCmp(lhsVal, rhsVal, .orderedEqual)
  62. *G4UBUFNFOUT define double @simpleIf(double %n) { entry: %cmp = fcmp

    one double %n, 0.000000e+00 def simpleIf(n) if n then n + 1 else 0
  63. *G4UBUFNFOUT define double @simpleIf(double %n) { entry: %cmp = fcmp

    one double %n, 0.000000e+00 br i1 %cmp, label %then, label %else def simpleIf(n) if n then n + 1 else 0
  64. *G4UBUFNFOUT define double @simpleIf(double %n) { entry: %cmp = fcmp

    one double %n, 0.000000e+00 br i1 %cmp, label %then, label %else then: %1 = fadd double %n, 1.000000e+00 br label %merge def simpleIf(n) if n then n + 1 else 0
  65. *G4UBUFNFOUT define double @simpleIf(double %n) { entry: %cmp = fcmp

    one double %n, 0.000000e+00 br i1 %cmp, label %then, label %else then: %1 = fadd double %n, 1.000000e+00 br label %merge else: br label %merge def simpleIf(n) if n then n + 1 else 0
  66. *G4UBUFNFOUT define double @simpleIf(double %n) { entry: %cmp = fcmp

    one double %n, 0.000000e+00 br i1 %cmp, label %then, label %else then: %1 = fadd double %n, 1.000000e+00 br label %merge else: br label %merge merge: %2 = phi double ret double %2 } def simpleIf(n) if n then n + 1 else 0 [ %1, %then ], [ 0.000000e+00, %else ]
  67. *G4UBUFNFOUT define double @simpleIf(double %n) { entry: %cmp = fcmp

    one double %n, 0.000000e+00 br i1 %cmp, label %then, label %else then: %1 = fadd double %n, 1.000000e+00 br label %merge else: br label %merge merge: %2 = phi double ret double %2 } def simpleIf(n) if n then n + 1 else 0 [ %1, %then ], [ 0.000000e+00, %else ]
  68. *G4UBUFNFOUT define double @simpleIf(double %n) { entry: %cmp = fcmp

    one double %n, 0.000000e+00 br i1 %cmp, label %then, label %else then: %1 = fadd double %n, 1.000000e+00 br label %merge else: br label %merge merge: %2 = phi double ret double %2 } def simpleIf(n) if n then n + 1 else 0 [ %1, %then ], [ 0.000000e+00, %else ]
  69. case .ifelse(let cond, let thenExpr, let elseExpr): let checkCond =

    builder.buildFCmp(try emitExpr(cond), FloatType.double.constant(0.0), .orderedNotEqual)
  70. case .ifelse(let cond, let thenExpr, let elseExpr): let checkCond =

    builder.buildFCmp(try emitExpr(cond), FloatType.double.constant(0.0), .orderedNotEqual) let thenBB = builder.currentFunction!.appendBasicBlock(named: "then") let elseBB = builder.currentFunction!.appendBasicBlock(named: "else") let mergeBB = builder.currentFunction!.appendBasicBlock(named: "merge")
  71. case .ifelse(let cond, let thenExpr, let elseExpr): let checkCond =

    builder.buildFCmp(try emitExpr(cond), FloatType.double.constant(0.0), .orderedNotEqual) let thenBB = builder.currentFunction!.appendBasicBlock(named: "then") let elseBB = builder.currentFunction!.appendBasicBlock(named: "else") let mergeBB = builder.currentFunction!.appendBasicBlock(named: "merge") builder.buildCondBr(condition: checkCond, then: thenBB, else: elseBB)
  72. case .ifelse(let cond, let thenExpr, let elseExpr): let checkCond =

    builder.buildFCmp(try emitExpr(cond), FloatType.double.constant(0.0), .orderedNotEqual) let thenBB = builder.currentFunction!.appendBasicBlock(named: "then") let elseBB = builder.currentFunction!.appendBasicBlock(named: "else") let mergeBB = builder.currentFunction!.appendBasicBlock(named: "merge") builder.buildCondBr(condition: checkCond, then: thenBB, else: elseBB) builder.positionAtEnd(of: thenBB) let thenVal = try emitExpr(thenExpr) builder.buildBr(mergeBB)
  73. case .ifelse(let cond, let thenExpr, let elseExpr): let checkCond =

    builder.buildFCmp(try emitExpr(cond), FloatType.double.constant(0.0), .orderedNotEqual) let thenBB = builder.currentFunction!.appendBasicBlock(named: "then") let elseBB = builder.currentFunction!.appendBasicBlock(named: "else") let mergeBB = builder.currentFunction!.appendBasicBlock(named: "merge") builder.buildCondBr(condition: checkCond, then: thenBB, else: elseBB) builder.positionAtEnd(of: thenBB) let thenVal = try emitExpr(thenExpr) builder.buildBr(mergeBB) builder.positionAtEnd(of: elseBB) let elseVal = try emitExpr(elseExpr) builder.buildBr(mergeBB)
  74. case .ifelse(let cond, let thenExpr, let elseExpr): let checkCond =

    builder.buildFCmp(try emitExpr(cond), FloatType.double.constant(0.0), .orderedNotEqual) let thenBB = builder.currentFunction!.appendBasicBlock(named: "then") let elseBB = builder.currentFunction!.appendBasicBlock(named: "else") let mergeBB = builder.currentFunction!.appendBasicBlock(named: "merge") builder.buildCondBr(condition: checkCond, then: thenBB, else: elseBB) builder.positionAtEnd(of: thenBB) let thenVal = try emitExpr(thenExpr) builder.buildBr(mergeBB) builder.positionAtEnd(of: elseBB) let elseVal = try emitExpr(elseExpr) builder.buildBr(mergeBB) builder.positionAtEnd(of: mergeBB)
  75. case .ifelse(let cond, let thenExpr, let elseExpr): let checkCond =

    builder.buildFCmp(try emitExpr(cond), FloatType.double.constant(0.0), .orderedNotEqual) let thenBB = builder.currentFunction!.appendBasicBlock(named: "then") let elseBB = builder.currentFunction!.appendBasicBlock(named: "else") let mergeBB = builder.currentFunction!.appendBasicBlock(named: "merge") builder.buildCondBr(condition: checkCond, then: thenBB, else: elseBB) builder.positionAtEnd(of: thenBB) let thenVal = try emitExpr(thenExpr) builder.buildBr(mergeBB) builder.positionAtEnd(of: elseBB) let elseVal = try emitExpr(elseExpr) builder.buildBr(mergeBB) builder.positionAtEnd(of: mergeBB) let phi = builder.buildPhi(FloatType.double) phi.addIncoming([(thenVal, thenBB), (elseVal, elseBB)])
  76. case .ifelse(let cond, let thenExpr, let elseExpr): let checkCond =

    builder.buildFCmp(try emitExpr(cond), FloatType.double.constant(0.0), .orderedNotEqual) let thenBB = builder.currentFunction!.appendBasicBlock(named: "then") let elseBB = builder.currentFunction!.appendBasicBlock(named: "else") let mergeBB = builder.currentFunction!.appendBasicBlock(named: "merge") builder.buildCondBr(condition: checkCond, then: thenBB, else: elseBB) builder.positionAtEnd(of: thenBB) let thenVal = try emitExpr(thenExpr) builder.buildBr(mergeBB) builder.positionAtEnd(of: elseBB) let elseVal = try emitExpr(elseExpr) builder.buildBr(mergeBB) builder.positionAtEnd(of: mergeBB) let phi = builder.buildPhi(FloatType.double) phi.addIncoming([(thenVal, thenBB), (elseVal, elseBB)]) return phi }
  77. func emitMain() throws { let mainType = FunctionType(argTypes: [], returnType:

    VoidType()) let function = builder.addFunction("main", type: mainType)
  78. func emitMain() throws { let mainType = FunctionType(argTypes: [], returnType:

    VoidType()) let function = builder.addFunction("main", type: mainType) let entry = function.appendBasicBlock(named: "entry") builder.positionAtEnd(of: entry) let formatString = builder.buildGlobalStringPtr("%f\n") let printf = emitPrintf()
  79. func emitMain() throws { let mainType = FunctionType(argTypes: [], returnType:

    VoidType()) let function = builder.addFunction("main", type: mainType) let entry = function.appendBasicBlock(named: "entry") builder.positionAtEnd(of: entry) let formatString = builder.buildGlobalStringPtr("%f\n") let printf = emitPrintf() for expr in file.expressions { let val = try emitExpr(expr) builder.buildCall(printf, args: [formatString, val]) }
  80. func emitMain() throws { let mainType = FunctionType(argTypes: [], returnType:

    VoidType()) let function = builder.addFunction("main", type: mainType) let entry = function.appendBasicBlock(named: "entry") builder.positionAtEnd(of: entry) let formatString = builder.buildGlobalStringPtr("%f\n") let printf = emitPrintf() builder.buildRetVoid() } for expr in file.expressions { let val = try emitExpr(expr) builder.buildCall(printf, args: [formatString, val]) }
  81. func emitMain() throws { let mainType = FunctionType(argTypes: [], returnType:

    VoidType()) let function = builder.addFunction("main", type: mainType) let entry = function.appendBasicBlock(named: "entry") builder.positionAtEnd(of: entry) let formatString = builder.buildGlobalStringPtr("%f\n") let printf = emitPrintf() builder.buildRetVoid() } func emit() throws { for extern in file.externs { emitPrototype(extern) } for definition in file.definitions { try emitDefinition(definition) } try emitMain() } for expr in file.expressions { let val = try emitExpr(expr) builder.buildCall(printf, args: [formatString, val]) }