Coder Social home page Coder Social logo

f2's Introduction

codename f2

f2 is the placeholder name for this language until I think of a cooler name.

This is a low-level language with compile time memory management that will soon be viable for general purpose development, with a focus on portability.

The main idea this language is pushing is that memory can be managed at compile time. We'll see how feasible this as we go.

Currently, the language is parsed into an AST using ANTLR, translated into an IR, and that IR is converted to LLVM IR.

The syntax is influenced by Haskell, C, Prolog, and everything else out there. Here's what the language can support:

struct X {
  a : Int32
}

struct Y {
  x : X
}

f :: X -> Int32
f x = x.a.

g :: Int32 -> X
g a = X{a}.

h :: Int32 -> Int32
h a = let x = X{a},
      x.a.

i :: Int32 -> Y
i a = Y{X{a}}.

j :: Int32 -> Int32
j a = let x = X{a},
      let y = Y{x},
      let w = y.x,
      w.a.

This has some functions that include heap and stack allocations. This is translated into IR, which can be printed out:

fun f(%0 : X) : Int32 ()
    FieldGetInstruction(debugInfo=(14,6), registerIndex=0, fieldIndex=0)
    StoreInstruction(debugInfo=(14,6), register=1)
    ReturnInstruction(debugInfo=(14,6), registerIndex=1)

fun g(%0 : Int32) : X ()
    HeapAllocateInstruction(debugInfo=(17,6), type=X)
    StoreInstruction(debugInfo=(17,6), register=1)
    FieldSetInstruction(debugInfo=(17,8), structRegisterIndex=1, fieldIndex=0, valueRegisterIndex=0)
    ReturnInstruction(debugInfo=(17,6), registerIndex=1)

fun h(%0 : Int32) : Int32 ()
    StackAllocateInstruction(debugInfo=(20,14), type=X)
    StoreInstruction(debugInfo=(20,14), register=1)
    FieldSetInstruction(debugInfo=(20,16), structRegisterIndex=1, fieldIndex=0, valueRegisterIndex=0)
    FieldGetInstruction(debugInfo=(21,6), registerIndex=1, fieldIndex=0)
    StoreInstruction(debugInfo=(21,6), register=2)
    ReturnInstruction(debugInfo=(21,6), registerIndex=2)

fun i(%0 : Int32) : Y ()
    HeapAllocateInstruction(debugInfo=(24,8), type=X)
    StoreInstruction(debugInfo=(24,8), register=1)
    FieldSetInstruction(debugInfo=(24,10), structRegisterIndex=1, fieldIndex=0, valueRegisterIndex=0)
    HeapAllocateInstruction(debugInfo=(24,6), type=Y)
    StoreInstruction(debugInfo=(24,6), register=2)
    FieldSetInstruction(debugInfo=(24,8), structRegisterIndex=2, fieldIndex=0, valueRegisterIndex=1)
    ReturnInstruction(debugInfo=(24,6), registerIndex=2)

fun j(%0 : Int32) : Int32 ()
    StackAllocateInstruction(debugInfo=(27,14), type=X)
    StoreInstruction(debugInfo=(27,14), register=1)
    FieldSetInstruction(debugInfo=(27,16), structRegisterIndex=1, fieldIndex=0, valueRegisterIndex=0)
    StackAllocateInstruction(debugInfo=(28,14), type=Y)
    StoreInstruction(debugInfo=(28,14), register=2)
    FieldSetInstruction(debugInfo=(28,16), structRegisterIndex=2, fieldIndex=0, valueRegisterIndex=1)
    FieldGetInstruction(debugInfo=(29,14), registerIndex=2, fieldIndex=0)
    StoreInstruction(debugInfo=(29,6), register=3)
    FieldGetInstruction(debugInfo=(30,6), registerIndex=3, fieldIndex=0)
    StoreInstruction(debugInfo=(30,6), register=4)
    ReturnInstruction(debugInfo=(30,6), registerIndex=4)

The IR is register (& stack?) based, which translates easily to LLVM IR:

define i32 @f(%X*) {
entry:
  %1 = getelementptr inbounds %X, %X* %0, i32 0, i32 0
  %2 = load i32, i32* %1
  ret i32 %2
}

define %X* @g(i32) {
entry:
  %1 = call i8* @malloc(i64 4)
  %2 = bitcast i8* %1 to %X*
  %3 = getelementptr inbounds %X, %X* %2, i32 0, i32 0
  store i32 %0, i32* %3
  ret %X* %2
}

define i32 @h(i32) {
entry:
  %1 = alloca %X
  %2 = getelementptr inbounds %X, %X* %1, i32 0, i32 0
  store i32 %0, i32* %2
  %3 = getelementptr inbounds %X, %X* %1, i32 0, i32 0
  %4 = load i32, i32* %3
  ret i32 %4
}

define %Y* @i(i32) {
entry:
  %1 = call i8* @malloc(i64 4)
  %2 = bitcast i8* %1 to %X*
  %3 = getelementptr inbounds %X, %X* %2, i32 0, i32 0
  store i32 %0, i32* %3
  %4 = call i8* @malloc(i64 8)
  %5 = bitcast i8* %4 to %Y*
  %6 = getelementptr inbounds %Y, %Y* %5, i32 0, i32 0
  store %X* %2, %X** %6
  ret %Y* %5
}

define i32 @j(i32) {
entry:
  %1 = alloca %X
  %2 = getelementptr inbounds %X, %X* %1, i32 0, i32 0
  store i32 %0, i32* %2
  %3 = alloca %Y
  %4 = getelementptr inbounds %Y, %Y* %3, i32 0, i32 0
  store %X* %1, %X** %4
  %5 = getelementptr inbounds %Y, %Y* %3, i32 0, i32 0
  %6 = load %X*, %X** %5
  %7 = getelementptr inbounds %X, %X* %6, i32 0, i32 0
  %8 = load i32, i32* %7
  ret i32 %8
}

Heap and stack allocations are properly translated to malloc and alloca. This part is easy. The more challenging part is knowing when to free this memory. Here's an example of one way we're freeing memory at compiletime:

struct X {
  a : Int32
}

box :: Int32 -> X
box a = X{a}. -- memory is allocated here

f :: Int32 -> Int32
f i = let x = box(i),
      let a = firstThing(x),
      let b = secondThing(x),
      let c = thirdThing(x), -- memory can be freed after this call
      a + b + c.

This is translated to:

define %X* @box(i32) {
entry:
  %1 = call i8* @malloc(i64 4) ; memory allocated on the heap here
  %2 = bitcast i8* %1 to %X*
  %3 = getelementptr inbounds %X, %X* %2, i32 0, i32 0
  store i32 %0, i32* %3
  ret %X* %2
}

define i32 @f(i32) {
entry:
  %1 = call %X* @box(i32 %0)
  %2 = call i32 @firstThing(%X* %1)
  %3 = call i32 @secondThing(%X* %1)
  %4 = call i32 @thirdThing(%X* %1)
  %5 = bitcast %X* %1 to i8*
  call void @free(i8* %5) ; that memory is freed here
  %6 = call i32 @internal_add_i32(i32 %3, i32 %4)
  %7 = call i32 @internal_add_i32(i32 %2, i32 %6)
  ret i32 %7
}

Boom! Memory freeing has been determined at compile time! This won't work for everything, and control hasn't been implemented, but it's getting there.

There are a lot more tests in the test suite (or at least there will be).

f2's People

Contributors

phase avatar

Stargazers

Andrew N avatar s0cks avatar abbie avatar Ryan W avatar

Watchers

James Cloos avatar  avatar

f2's Issues

Traits & Generics & LLVM

One way to implement Traits would to be how Classes are implemented in C++.
This doesn't show how multiple Traits would work.

Here's some example code.
clang -S -emit-llvm _.cpp to compile to IR, opt _.ll -S -o _-opt.ll -mem2reg to optimize it.

class Interface {
  public:
    Interface(){}
    virtual ~Interface(){}
    virtual int foo(int)=0;
    virtual void bar(int)=0;
};

class Concrete : public Interface {
  private:
    int x;

  public:
    Concrete(){}
    ~Concrete(){}
    int foo(int);
    void bar(int);
};

int Concrete::foo(int x) {
  int y = x + 80;
  return y;
}

void Concrete::bar(int x) {
  int y = x + 81;
}

int main() {
  Interface *f = new Concrete();

  int x = f->foo(82);
  f->bar(83);

  delete f;

  return 0;
}

I've added comments that contain the demangled names.

; ModuleID = 'interface.ll'
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-pc-linux-gnu"

%class.Concrete = type <{ %class.Interface, i32, [4 x i8] }>
%class.Interface = type { i32 (...)** }

$_ZN8ConcreteC2Ev = comdat any

$_ZN8ConcreteD2Ev = comdat any

$_ZN8ConcreteD0Ev = comdat any

$_ZN9InterfaceC2Ev = comdat any

$_ZN9InterfaceD2Ev = comdat any

$_ZN9InterfaceD0Ev = comdat any

$_ZTS9Interface = comdat any

$_ZTI9Interface = comdat any

$_ZTV9Interface = comdat any

@_ZTV8Concrete = unnamed_addr constant [6 x i8*] [i8* null, i8* bitcast ({ i8*, i8*, i8* }* @_ZTI8Concrete to i8*), i8* bitcast (void (%class.Concrete*)* @_ZN8ConcreteD2Ev to i8*), i8* bitcast (void (%class.Concrete*)* @_ZN8ConcreteD0Ev to i8*), i8* bitcast (i32 (%class.Concrete*, i32)* @_ZN8Concrete3fooEi to i8*), i8* bitcast (void (%class.Concrete*, i32)* @_ZN8Concrete3barEi to i8*)], align 8
@_ZTVN10__cxxabiv120__si_class_type_infoE = external global i8*
@_ZTS8Concrete = constant [10 x i8] c"8Concrete\00"
@_ZTVN10__cxxabiv117__class_type_infoE = external global i8*
@_ZTS9Interface = linkonce_odr constant [11 x i8] c"9Interface\00", comdat
@_ZTI9Interface = linkonce_odr constant { i8*, i8* } { i8* bitcast (i8** getelementptr inbounds (i8*, i8** @_ZTVN10__cxxabiv117__class_type_infoE, i64 2) to i8*), i8* getelementptr inbounds ([11 x i8], [11 x i8]* @_ZTS9Interface, i32 0, i32 0) }, comdat
@_ZTI8Concrete = constant { i8*, i8*, i8* } { i8* bitcast (i8** getelementptr inbounds (i8*, i8** @_ZTVN10__cxxabiv120__si_class_type_infoE, i64 2) to i8*), i8* getelementptr inbounds ([10 x i8], [10 x i8]* @_ZTS8Concrete, i32 0, i32 0), i8* bitcast ({ i8*, i8* }* @_ZTI9Interface to i8*) }
@_ZTV9Interface = linkonce_odr unnamed_addr constant [6 x i8*] [i8* null, i8* bitcast ({ i8*, i8* }* @_ZTI9Interface to i8*), i8* bitcast (void (%class.Interface*)* @_ZN9InterfaceD2Ev to i8*), i8* bitcast (void (%class.Interface*)* @_ZN9InterfaceD0Ev to i8*), i8* bitcast (void ()* @__cxa_pure_virtual to i8*), i8* bitcast (void ()* @__cxa_pure_virtual to i8*)], comdat, align 8

; Concrete::foo(int)
; Function Attrs: nounwind uwtable
define i32 @_ZN8Concrete3fooEi(%class.Concrete* %this, i32 %x) unnamed_addr #0 align 2 {
  %1 = add nsw i32 %x, 80
  ret i32 %1
}

; Concrete::bar(int)
; Function Attrs: nounwind uwtable
define void @_ZN8Concrete3barEi(%class.Concrete* %this, i32 %x) unnamed_addr #0 align 2 {
  %1 = add nsw i32 %x, 81
  ret void
}

; Function Attrs: norecurse uwtable
define i32 @main() #1 personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*) {
  %1 = call noalias i8* @_Znwm(i64 16) #5
  %2 = bitcast i8* %1 to %class.Concrete*
  invoke void @_ZN8ConcreteC2Ev(%class.Concrete* %2)
          to label %3 unwind label %21

; <label>:3                                       ; preds = %0
  %4 = bitcast %class.Concrete* %2 to %class.Interface*
  %5 = bitcast %class.Interface* %4 to i32 (%class.Interface*, i32)***
  %6 = load i32 (%class.Interface*, i32)**, i32 (%class.Interface*, i32)*** %5, align 8
  %7 = getelementptr inbounds i32 (%class.Interface*, i32)*, i32 (%class.Interface*, i32)** %6, i64 2
  %8 = load i32 (%class.Interface*, i32)*, i32 (%class.Interface*, i32)** %7, align 8
  %9 = call i32 %8(%class.Interface* %4, i32 82)
  %10 = bitcast %class.Interface* %4 to void (%class.Interface*, i32)***
  %11 = load void (%class.Interface*, i32)**, void (%class.Interface*, i32)*** %10, align 8
  %12 = getelementptr inbounds void (%class.Interface*, i32)*, void (%class.Interface*, i32)** %11, i64 3
  %13 = load void (%class.Interface*, i32)*, void (%class.Interface*, i32)** %12, align 8
  call void %13(%class.Interface* %4, i32 83)
  %14 = icmp eq %class.Interface* %4, null
  br i1 %14, label %20, label %15

; <label>:15                                      ; preds = %3
  %16 = bitcast %class.Interface* %4 to void (%class.Interface*)***
  %17 = load void (%class.Interface*)**, void (%class.Interface*)*** %16, align 8
  %18 = getelementptr inbounds void (%class.Interface*)*, void (%class.Interface*)** %17, i64 1
  %19 = load void (%class.Interface*)*, void (%class.Interface*)** %18, align 8
  call void %19(%class.Interface* %4)
  br label %20

; <label>:20                                      ; preds = %15, %3
  ret i32 0

; <label>:21                                      ; preds = %0
  %22 = landingpad { i8*, i32 }
          cleanup
  %23 = extractvalue { i8*, i32 } %22, 0
  %24 = extractvalue { i8*, i32 } %22, 1
  call void @_ZdlPv(i8* %1) #6
  br label %25

; <label>:25                                      ; preds = %21
  %26 = insertvalue { i8*, i32 } undef, i8* %23, 0
  %27 = insertvalue { i8*, i32 } %26, i32 %24, 1
  resume { i8*, i32 } %27
}

; operator new(unsigned long)
; Function Attrs: nobuiltin
declare noalias i8* @_Znwm(i64) #2

; Concrete::Concrete()
; Function Attrs: uwtable
define linkonce_odr void @_ZN8ConcreteC2Ev(%class.Concrete* %this) unnamed_addr #3 comdat align 2 {
  %1 = bitcast %class.Concrete* %this to %class.Interface*
  call void @_ZN9InterfaceC2Ev(%class.Interface* %1)
  %2 = bitcast %class.Concrete* %this to i32 (...)***
  store i32 (...)** bitcast (i8** getelementptr inbounds ([6 x i8*], [6 x i8*]* @_ZTV8Concrete, i64 0, i64 2) to i32 (...)**), i32 (...)*** %2, align 8
  ret void
}

declare i32 @__gxx_personality_v0(...)

; operator delete(void*)
; Function Attrs: nobuiltin nounwind
declare void @_ZdlPv(i8*) #4

; Concrete::~Concrete()
; Function Attrs: nounwind uwtable
define linkonce_odr void @_ZN8ConcreteD2Ev(%class.Concrete* %this) unnamed_addr #0 comdat align 2 {
  %1 = bitcast %class.Concrete* %this to %class.Interface*
  call void @_ZN9InterfaceD2Ev(%class.Interface* %1)
  ret void
}

; Concrete::~Concrete()
; Function Attrs: uwtable
define linkonce_odr void @_ZN8ConcreteD0Ev(%class.Concrete* %this) unnamed_addr #3 comdat align 2 personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*) {
  invoke void @_ZN8ConcreteD2Ev(%class.Concrete* %this)
          to label %1 unwind label %3

; <label>:1                                       ; preds = %0
  %2 = bitcast %class.Concrete* %this to i8*
  call void @_ZdlPv(i8* %2) #6
  ret void

; <label>:3                                       ; preds = %0
  %4 = landingpad { i8*, i32 }
          cleanup
  %5 = extractvalue { i8*, i32 } %4, 0
  %6 = extractvalue { i8*, i32 } %4, 1
  %7 = bitcast %class.Concrete* %this to i8*
  call void @_ZdlPv(i8* %7) #6
  br label %8

; <label>:8                                       ; preds = %3
  %9 = insertvalue { i8*, i32 } undef, i8* %5, 0
  %10 = insertvalue { i8*, i32 } %9, i32 %6, 1
  resume { i8*, i32 } %10
}

; Interface::Interface()
; Function Attrs: nounwind uwtable
define linkonce_odr void @_ZN9InterfaceC2Ev(%class.Interface* %this) unnamed_addr #0 comdat align 2 {
  %1 = bitcast %class.Interface* %this to i32 (...)***
  store i32 (...)** bitcast (i8** getelementptr inbounds ([6 x i8*], [6 x i8*]* @_ZTV9Interface, i64 0, i64 2) to i32 (...)**), i32 (...)*** %1, align 8
  ret void
}

; Interface::~Interface()
; Function Attrs: nounwind uwtable
define linkonce_odr void @_ZN9InterfaceD2Ev(%class.Interface* %this) unnamed_addr #0 comdat align 2 {
  ret void
}

; Interface::~Interface()
; Function Attrs: uwtable
define linkonce_odr void @_ZN9InterfaceD0Ev(%class.Interface* %this) unnamed_addr #3 comdat align 2 personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*) {
  invoke void @_ZN9InterfaceD2Ev(%class.Interface* %this)
          to label %1 unwind label %3

; <label>:1                                       ; preds = %0
  %2 = bitcast %class.Interface* %this to i8*
  call void @_ZdlPv(i8* %2) #6
  ret void

; <label>:3                                       ; preds = %0
  %4 = landingpad { i8*, i32 }
          cleanup
  %5 = extractvalue { i8*, i32 } %4, 0
  %6 = extractvalue { i8*, i32 } %4, 1
  %7 = bitcast %class.Interface* %this to i8*
  call void @_ZdlPv(i8* %7) #6
  br label %8

; <label>:8                                       ; preds = %3
  %9 = insertvalue { i8*, i32 } undef, i8* %5, 0
  %10 = insertvalue { i8*, i32 } %9, i32 %6, 1
  resume { i8*, i32 } %10
}

declare void @__cxa_pure_virtual()

attributes #0 = { nounwind uwtable "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+fxsr,+mmx,+sse,+sse2" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #1 = { norecurse uwtable "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+fxsr,+mmx,+sse,+sse2" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #2 = { nobuiltin "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+fxsr,+mmx,+sse,+sse2" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #3 = { uwtable "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+fxsr,+mmx,+sse,+sse2" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #4 = { nobuiltin nounwind "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+fxsr,+mmx,+sse,+sse2" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #5 = { builtin }
attributes #6 = { builtin nounwind }

!llvm.ident = !{!0}

!0 = !{!"clang version 3.8.0-2ubuntu4 (tags/RELEASE_380/final)"}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.