1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453
|
#pragma once
#include "Core/Object.h"
#include "Core/Array.h"
#include "TypeDesc.h"
#include "Instr.h"
#include "Label.h"
#include "Var.h"
#include "Block.h"
namespace code {
STORM_PKG(core.asm);
class Arena;
/**
* Control when and how variables are freed.
*/
enum FreeOpt {
STORM_NAME(freeOnNone, none) = 0x0,
STORM_NAME(freeOnException, exception) = 0x1,
STORM_NAME(freeOnBlockExit, blockExit) = 0x2,
STORM_NAME(freeOnBoth, both) = 0x3,
// Pass a pointer to the free-function?
STORM_NAME(freePtr, ptr) = 0x10,
// Used by the backends: What is stored in the variable is actually a pointer to the value.
STORM_NAME(freeIndirection, indirection) = 0x20,
// Used by the backends: Do not initialize this variable.
STORM_NAME(freeNoInit, noInit) = 0x40,
// Default options.
STORM_NAME(freeDef, default) = freeOnBoth,
// This variable needs to be activated before it will be freed.
STORM_NAME(freeInactive, inactive) = 0x80,
};
BITMASK_OPERATORS(FreeOpt);
// Note: Both of these are intended for convenience from C++. They differ slightly from the
// standard output from a FreeOpt. They are mainly used for outputting a Listing, so this is fine.
wostream &operator <<(wostream &to, FreeOpt o);
StrBuf &operator <<(StrBuf &to, FreeOpt o);
/**
* Represents a code listing along with information about blocks and variables. Can be linked
* into machine code.
*
* A listing contains a set of labels. Each instruction may have zero or more labels attached to
* them. The labels can then be referenced inside that listing, which creates a reference to the
* start of the attached instruction.
*
* A listing also contains a set of blocks. By default, there is a root block. Each block may
* then contain zero or more nested blocks, and zero or more variables. The root block is active
* from the prolog() instruction to the epilog() instruction. Whenever a block is active, all
* variables inside it and its parent blocks are accessible.
*
* Each variable may also be associated with an "activation point" by setting "freeInactive"
* flag on the variable. This means that the variable will be accessible as usual, but that it
* will not be destroyed until it has been explicitly activated. This is used to properly handle
* values that need to be present in memory when initializing them, but destruction shall not be
* done until the constructor is finished. Variables are activated using the activate()
* pseudo-instruction. As with blocks, these are interpreted in a lexical scope. I.e. a variable
* is considered inactive in all locations before the activate() instruction and active in all
* locations after activate(), regardless of jumps and which blocks are entered.
*
* Note: any labels which have not had an instruction added after them are considered
* unused. Ie. it is not possible to reference the end of the listing, as that feature is seldom
* used. If you need labels to the end, consider adding a dummy dat(0) at the end.
*/
class Listing : public Object {
STORM_CLASS;
public:
// Create an empty listing. Optionally associating it with an arena. Doing that makes the
// listing show backend-specific things in a proper way.
STORM_CTOR Listing();
STORM_CTOR Listing(Bool member, TypeDesc *result);
STORM_CTOR Listing(const Arena *arena);
STORM_CTOR Listing(const Arena *arena, Bool member, TypeDesc *result);
Listing(const Listing &o);
// Deep copy.
virtual void STORM_FN deepCopy(CloneEnv *env);
/**
* One entry in the listing:
*/
class Entry {
STORM_VALUE;
public:
STORM_CTOR Entry();
STORM_CTOR Entry(Instr *instr);
STORM_CTOR Entry(Instr *instr, MAYBE(Array<Label> *) labels);
// Deep copy.
void STORM_FN deepCopy(CloneEnv *env);
// The instruction at this point.
Instr *instr;
// Labels before this instruction (if any). Only created if any labels are added.
MAYBE(Array<Label> *) labels;
// Add a label here.
void add(Engine &e, Label label);
// Output.
void STORM_FN toS(StrBuf *to) const;
};
// Add instructions and labels.
Listing &STORM_FN operator <<(Instr *op);
Listing &STORM_FN operator <<(Label l);
Listing &STORM_FN operator <<(MAYBE(Array<Label> *) l);
// Insert instructions and labels.
void STORM_FN insert(Nat pos, Instr *op);
void STORM_FN insert(Nat pos, Label l);
// Access instructions.
inline Nat STORM_FN count() const { return code->count(); }
inline Bool STORM_FN empty() const { return count() == 0; }
inline Bool STORM_FN any() const { return count() > 0; }
inline Instr *STORM_FN operator [](Nat id) const { return at(id); }
inline Instr *at(Nat id) const { return code->at(id).instr; }
// Modify instruction (internal use).
inline void setInstr(Nat id, Instr *to) { code->at(id).instr = to; }
// Give `id = count()` to access any labels after the last instruction.
MAYBE(Array<Label> *) STORM_FN labels(Nat id) const;
// Create a shell, ie. a Listing containing only the scope information from this listing,
// thus making variables, blocks from this listing are valid in the shell as well.
Listing *STORM_FN createShell() const;
Listing *STORM_FN createShell(MAYBE(const Arena *) arena) const;
/**
* Labels.
*/
// Label reserved for metadata.
Label STORM_FN meta();
// Create a new label.
Label STORM_FN label();
// Get the number of labels allocated (i.e., id:s 0 to labelCount - 1 are used).
Nat STORM_FN labelCount() const;
/**
* Additional source-level information about variables. Front-ends may tag variables in this
* way to provide a better debugging experience, but doing so is optional.
*/
class VarInfo : public Object {
STORM_CLASS;
public:
// Name of the variable.
Str *name;
// Type of the variable.
Type *type;
// Reference?
Bool ref;
// Source location.
SrcPos pos;
// Create.
STORM_CTOR VarInfo(Str *name, Type *type, Bool ref, SrcPos pos);
// Deep copy.
void STORM_FN deepCopy(CloneEnv *env);
};
/**
* Scope management.
*/
// Get the root block.
Block STORM_FN root() const;
// Create a block inside `parent`. Note that it does not matter if `parent` is the block or
// any part inside that block.
Block STORM_FN createBlock(Block parent);
// Move a parameter to a specific location.
void STORM_FN moveParam(Var param, Nat to);
// Move a variable to the beginning of a block. This impacts memory layout and destruction order.
void STORM_FN moveFirst(Var var);
// Get the variable stored just before `v` in this stack frame. Within a single block, it
// just returns the variable added before `v`. If `v` is the first variable in that block,
// the last variable of the previous block is returned. This will give all variables visible
// at the same time as the start variable of the iteration. Parameters are returned lastly.
Var STORM_FN prev(Var v) const;
// Get the parent part to a variable or a block.
Block STORM_FN parent(Block b) const;
Block STORM_FN parent(Var b) const;
// See if the variable `v` is accessible in the part `p`. This is almost equivalent to
// checking if any parent blocks of `p` contains the variable.
Bool STORM_FN accessible(Var v, Block p) const;
// See if the part `q` is an indirect parent to `parent`.
Bool STORM_FN isParent(Block parent, Block q) const;
// Is this a parameter?
Bool STORM_FN isParam(Var v) const;
// Get parameter info about a variable.
MAYBE(TypeDesc *) STORM_FN paramDesc(Var v) const;
// Get the parameter index of a variable. Performs a linear search, so might not be
// performant for large parameter lists.
Nat STORM_FN paramIndex(Var v) const;
// Get debug info about a variable.
MAYBE(VarInfo *) STORM_FN varInfo(Var v) const;
// Set debug info.
void STORM_FN varInfo(Var v, MAYBE(VarInfo *) info);
// Get all blocks.
Array<Block> *STORM_FN allBlocks() const;
// Get all variables. Always in order, so allVars()[i].key() == i
Array<Var> *STORM_FN allVars() const;
// Get all variables in a block.
Array<Var> *STORM_FN allVars(Block b) const;
// Get all parameters.
Array<Var> *STORM_FN allParams() const;
// Get the destructor for a variable or a parameter.
Operand STORM_FN freeFn(Var v) const;
// Get when to free a variable or a parameter.
FreeOpt STORM_FN freeOpt(Var v) const;
void STORM_FN freeOpt(Var v, FreeOpt opt);
/**
* Information of how to catch exceptions.
*
* All catch clauses have the following form:
* If an exception of type <type> is thrown, goto <resume> which is assumed to reside in the
* block level immediately outside the current block.
*/
class CatchInfo {
STORM_VALUE;
public:
// Create. `type` is the type to catch, `resume` is where to resume execution.
STORM_CTOR CatchInfo(Type *type, Label resume);
// Type.
Type *type;
// Resume. Assumed to refer to a location in a block immediately outside the block used
// as a catch handler.
Label resume;
};
// Add a catch handler to a block. Multiple handlers may be added to a single block. If so,
// they are evaluated in the order they were added.
void STORM_FN addCatch(Block block, CatchInfo add);
// Convenience function to create a `CatchInfo` value.
void STORM_FN addCatch(Block block, Type *type, Label resume);
// Get all catch clauses for a block.
MAYBE(Array<CatchInfo> *) STORM_FN catchInfo(Block block) const;
// Does this block need cleanup during exception handling?
inline Bool STORM_FN exceptionCleanup() const { return ehClean; }
// Does this block catch exceptions?
inline Bool STORM_FN exceptionCaught() const { return ehCatch; }
// Does this block deal with exceptions at all? I.e. is either `exceptionCleanup` or `exceptionCatched` true?
inline Bool STORM_FN exceptionAware() const { return ehCatch | ehClean; }
/**
* Create variables.
*/
inline Var STORM_FN createVar(Block in, Size size) { return createVar(in, size, Operand(), freeDef); }
inline Var STORM_FN createVar(Block in, Size size, Operand free) { return createVar(in, size, free, freeDef); }
Var STORM_FN createVar(Block in, Size size, Operand free, FreeOpt when);
Var STORM_FN createVar(Block in, TypeDesc *type);
Var STORM_FN createVar(Block in, TypeDesc *type, FreeOpt when);
Var STORM_FN createParam(TypeDesc *type);
Var STORM_FN createParam(TypeDesc *type, FreeOpt when);
Var STORM_FN createParam(TypeDesc *type, Operand free);
Var STORM_FN createParam(TypeDesc *type, Operand free, FreeOpt when);
/**
* Convenience functions.
*/
inline Var STORM_FN createByteVar(Block in) {
return createVar(in, Size::sByte);
}
inline Var STORM_FN createIntVar(Block in) {
return createVar(in, Size::sInt);
}
inline Var STORM_FN createLongVar(Block in) {
return createVar(in, Size::sLong);
}
inline Var STORM_FN createFloatVar(Block in) {
return createVar(in, Size::sFloat);
}
inline Var STORM_FN createByteParam() {
return createParam(new (this) PrimitiveDesc(bytePrimitive()));
}
inline Var STORM_FN createIntParam() {
return createParam(new (this) PrimitiveDesc(intPrimitive()));
}
inline Var STORM_FN createPtrParam() {
return createParam(new (this) PrimitiveDesc(ptrPrimitive()));
}
inline Var STORM_FN createLongParam() {
return createParam(new (this) PrimitiveDesc(longPrimitive()));
}
inline Var STORM_FN createFloatParam() {
return createParam(new (this) PrimitiveDesc(floatPrimitive()));
}
/**
* Result from this listing.
*/
// Resulting type from this listing.
TypeDesc *result;
// Is this function a member of a class?
Bool member;
/**
* Misc.
*/
// Output.
virtual void STORM_FN toS(StrBuf *to) const;
// Arena.
MAYBE(const Arena *const) arena;
private:
// Variable information.
class IVar {
STORM_VALUE;
public:
// Block containing this variable.
Nat parent;
// Size of this variable.
Size size;
// Parameter? If so: description of the parameter.
MAYBE(TypeDesc *) param;
// Optional variable information.
MAYBE(VarInfo *) info;
// Function to call on free.
Operand freeFn;
// When to free?
FreeOpt freeOpt;
// Create.
IVar(Nat parent, Size size, TypeDesc *param, Operand freeFn, FreeOpt opt);
// Deep copy.
void STORM_FN deepCopy(CloneEnv *env);
};
// Block information.
class IBlock {
STORM_VALUE;
public:
// Parent block.
Nat parent;
// All variables declared here.
Array<Nat> *vars;
// Catch handlers, if any.
MAYBE(Array<CatchInfo> *) catchInfo;
// Create.
IBlock(Engine &e);
IBlock(Engine &e, Nat parent);
// Deep copy.
void STORM_FN deepCopy(CloneEnv *env);
};
// Instructions and labels in here.
Array<Entry> *code;
// Labels added at the end (if any).
Array<Label> *nextLabels;
// Next free label id.
Nat nextLabel;
// Store variables and blocks.
Array<Nat> *params;
Array<IVar> *vars;
Array<IBlock> *blocks;
// Do we need exception cleanup?
Bool ehClean;
// Do we catch exceptions?
Bool ehCatch;
// Initialize everything.
void init(Bool member, TypeDesc *result);
// Find an ID in a Nat array. Returns size if none exists.
static Nat findId(Array<Nat> *in, Nat val);
// Create a variable from its index.
Var createVar(Nat index) const;
/**
* Output helpers.
*/
void putBlock(StrBuf &to, Nat block) const;
void putVar(StrBuf &to, Nat var) const;
};
// Insert a nop instruction if the last instruction was a call instruction.
// This is needed on some backends because the function return addresses are
// otherwise treated as if the next instruction faulted and is to be re-tried.
void STORM_FN padCallWithNop(Listing *dest);
}
#include "InstrFwd.h"
|