sslang-0.1.0.0
Safe HaskellNone
LanguageHaskell2010

Codegen.Codegen

Description

Translate SSM program to C compilation unit.

What is expected of the IR:

Well-formed: All primitive functions are applied to the right number of arguments.

Pure par expression: All par-expressions' operands are applications that have no side effects.

Defunctionalized: No lambdas; the only terms with an arrow type are variables or applications.

Name mangled: All variable identifiers are unique.

Synopsis

Documentation

todo :: HasCallStack => a Source #

Possible, but temporarily punted for the sake of expediency.

nope :: HasCallStack => a Source #

Impossible without a discussion about implementation strategy.

newtype EscExp Source #

Hack to allow us to splice string literals into C AST

Constructors

EscExp String 

Instances

Instances details
ToExp EscExp Source # 
Instance details

Defined in Codegen.Codegen

Methods

toExp :: EscExp -> SrcLoc -> Exp

data GenFnState Source #

State maintained while compiling a top-level SSM function.

The information here is populated while generating the step function, so that should be computed first, before this information is used to generate the act struct and enter definitions.

Constructors

GenFnState 

Fields

Instances

Instances details
MonadState GenFnState GenFn Source # 
Instance details

Defined in Codegen.Codegen

Methods

get :: GenFn GenFnState #

put :: GenFnState -> GenFn () #

state :: (GenFnState -> (a, GenFnState)) -> GenFn a #

newtype GenFn a Source #

Translation monad for procedures, with derived typeclass instances.

We declare GenFn as a newtype so that we can implement MonadFail for it, allowing us to use monadic pattern matching.

Constructors

GenFn (StateT GenFnState Pass a) 

Instances

Instances details
Monad GenFn Source # 
Instance details

Defined in Codegen.Codegen

Methods

(>>=) :: GenFn a -> (a -> GenFn b) -> GenFn b #

(>>) :: GenFn a -> GenFn b -> GenFn b #

return :: a -> GenFn a #

Functor GenFn Source # 
Instance details

Defined in Codegen.Codegen

Methods

fmap :: (a -> b) -> GenFn a -> GenFn b #

(<$) :: a -> GenFn b -> GenFn a #

MonadFail GenFn Source # 
Instance details

Defined in Codegen.Codegen

Methods

fail :: String -> GenFn a #

Applicative GenFn Source # 
Instance details

Defined in Codegen.Codegen

Methods

pure :: a -> GenFn a #

(<*>) :: GenFn (a -> b) -> GenFn a -> GenFn b #

liftA2 :: (a -> b -> c) -> GenFn a -> GenFn b -> GenFn c #

(*>) :: GenFn a -> GenFn b -> GenFn b #

(<*) :: GenFn a -> GenFn b -> GenFn a #

MonadState GenFnState GenFn Source # 
Instance details

Defined in Codegen.Codegen

Methods

get :: GenFn GenFnState #

put :: GenFnState -> GenFn () #

state :: (GenFnState -> (a, GenFnState)) -> GenFn a #

MonadError Error GenFn Source # 
Instance details

Defined in Codegen.Codegen

Methods

throwError :: Error -> GenFn a #

catchError :: GenFn a -> (Error -> GenFn a) -> GenFn a #

MonadWriter [Warning] GenFn Source # 
Instance details

Defined in Codegen.Codegen

Methods

writer :: (a, [Warning]) -> GenFn a #

tell :: [Warning] -> GenFn () #

listen :: GenFn a -> GenFn (a, [Warning]) #

pass :: GenFn (a, [Warning] -> [Warning]) -> GenFn a #

runGenFn Source #

Arguments

:: VarId

Name of procedure

-> [Binder Type]

Names and types of parameters to procedure

-> Expr Type

Body of procedure

-> TypegenInfo

Type information

-> [(VarId, Type)]

Other global identifiers

-> GenFn a

Translation monad to run

-> Pass a

Pass on errors to caller

Run a GenFn computation on a procedure.

getsTCon :: (TConInfo -> a) -> TConId -> GenFn a Source #

Lookup some information associated with a type constructor.

getsDCon :: (DConInfo -> a) -> DConId -> GenFn a Source #

Lookup some information associated with a data constructor.

nextCase :: GenFn Int Source #

Read and increment the number of cases in a procedure, i.e., fnCases++.

getFresh :: GenFn Int Source #

Obtain fresh integer in the GenFn monad

addBinding :: Binder Type -> Exp -> GenFn () Source #

Bind a variable to a C expression.

addLocal :: VarId -> Type -> GenFn VarId Source #

Register a new local variable, to be declared in activation record.

withBindings :: [(Binder Type, Exp)] -> GenFn a -> GenFn a Source #

Bind a variable to a C expression only while computing the given monad.

withNewLocal :: (VarId, Type) -> GenFn a -> GenFn a Source #

Register a local variable and bind its C expression during a monad.

maxWait :: Int -> GenFn () Source #

Register number of wait statements track of number of triggers needed.

freshLabel :: GenFn CIdent Source #

Generate a fresh label.

genTmp :: Type -> GenFn Exp Source #

Generate anonymous local variable in activation record for storage.

genParams :: [Binder Type] -> [(CIdent, Type)] Source #

Translate a list of SSM parameters to C parameters.

genLocals :: [(VarId, Type)] -> [(CIdent, Type)] Source #

Translate a list of SSM local declarations to C declarations.

genTrigs :: Int -> [(CIdent, Type)] Source #

Generate declarations for numTrigs triggers.

unit :: Exp Source #

The constant unit value, the singleton inhabitant of the type Unit.

undef :: Exp Source #

Fake undefined value used for expressions of type Void.

genProgram :: Program Type -> Pass [Definition] Source #

Generate a C compilation from an SSM program.

Each top-level function in a program is turned into three components:

  1. a struct (the activation record);
  2. an initialization function (the enter function); and
  3. a step function, which corresponds to the actual procedure body.

Items 2 and 3 include both declarations and definitions.

includes :: [Definition] Source #

Include statements in the generated C file.

genInitProgram :: VarId -> [Definition] Source #

Setup the entry point of the program.

genExtern :: (VarId, Type) -> Pass Definition Source #

genStruct :: GenFn Definition Source #

Generate struct definition for an SSM procedure.

This is where local variables, triggers, and parameter values are stored.

genEnter :: GenFn (Definition, Definition) Source #

Generate the enter function for an SSM procedure and its signature.

Its struct is allocated and initialized (partially; local variables' values are left uninitialized).

genStaticClosure :: GenFn (Definition, Definition) Source #

Generate static closure for top-level function

genStep :: GenFn (Definition, Definition) Source #

Generate the step function for an SSM procedure.

This function just defines the function definition and switch statement that wraps the statements of the procedure. The heavy lifting is performed by genExpr.

genYield :: GenFn [BlockItem] Source #

Helper to generate yield point in step function.

genExpr :: Expr Type -> GenFn (Exp, [BlockItem]) Source #

Translate an SSM expression into a C expression and statements.

SSM IR is a side-effectful expression language, with two implications when translating to C:

  1. every expression has a value (even if it is an uninhabited type), so this must be reflected in C; and
  2. some of the side effects in SSM cannot be implemented in C using expressions alone.

These two implications roughly translate to the C.Exp and [C.BlockItem] in genExpr's return type. When we translate an SSM expression e:

(val, stms) <- genExpr e

val represents the C expression that corresponds to the value of e upon evaluation, while stms represents the list of preceding statements that compute val.

A further consideration upon point 2 is that SSM expressions may yield control at any point. Thus, the C expression returned by genExpr must accommodate the step function suspending and resuming. For instance, consider the following SSM IR expression:

(let x = 3 in x) + (wait r; 6)

The x in the let-binding in the left operand cannot just be a local variable in the step function, because it would be "uninitialized" by the yield in the right operand:

  // let x = 3 in x
  // stms:
  int x = 3;
  // exp: x

  // (wait r; 6)
  // stms:
  ssm_sensitize(r);
  actg->pc = N;
  return;
case N:
  ssm_desensitize(r);
  // exp: 6

  // After the return, x is no longer initialized, so the following is
  // undefined behavior:
  x + 6

To ensure this is cannot happen, we conservatively declare x as a local variable in the activation record, so that its value is preserved between yields, even if this is not usually necessary. We leave it to later compiler passes to optimize this.

genPrim :: Primitive -> [Expr Type] -> Type -> GenFn (Exp, [BlockItem]) Source #

Generate code for SSM primitive; see genExpr for extended discussion.

genLiteral :: Literal -> Exp Source #

Generate C value for SSM literal, marshalled.

genLiteralRaw :: Literal -> Exp Source #

Generate C value for SSM literal, unmarshalled.

genPrimOp :: PrimOp -> [Expr Type] -> Type -> GenFn (Exp, [BlockItem]) Source #

Generate C expression for SSM primitive operation.

genBinop :: Expr Type -> Expr Type -> GenFn ((Exp, Exp), [BlockItem]) Source #

Helper for sequencing across binary operations.

genParArgs :: Int -> (Exp, Exp) -> [(Exp, Exp)] Source #

Compute priority and depth arguments for a par fork of given width.

depthSub :: Int -> Exp Source #

How much the depth should be decreased when par forking given width.