Skip to content

Instantly share code, notes, and snippets.

@zoecarver
Created April 13, 2020 20:54
Show Gist options
  • Save zoecarver/b00623d39d1f99c69bcfb423469ec024 to your computer and use it in GitHub Desktop.
Save zoecarver/b00623d39d1f99c69bcfb423469ec024 to your computer and use it in GitHub Desktop.

ParserSIL

Problem Statement

The SIL parser is one 7000 line file. It attempts to do three things: read SIL text, check and verify the input, and transform the input into SIL instructions/functions/etc. There are, at least, five overarching problems with the current implementation:

  1. The current implementation is old, complicated, and monolithic.
  2. The parser is a standalone library (for no good reason) which complicates the build system.
  3. It is hard to have "drop-in" tooling with this implementation.
  4. The current implementation has no interoperability with the serializer.
  5. The current implementation tries to do everything in one place: read, check and parse.

Solution

I'd like to propose a solution to incrementally fix all these issues. First, we'll move the parser into lib/Parse unifying the libraries.

Second I'll update the parser to model a new flow of information. This model has three steps: read, check, parse and allows for drop-in tooling at any point. This will allow us to do large-scale transformations, pretty-printing, and utilize other tools for easier debugging and maintenance. Here's a pseudo-code example of the proposed implementation:

Read text:

Input

sil @foo {
bb0():
	x = copy_value y
	store x to [init] z
}

Read

{ OperandIDs, Attrs } readCopyValue() {
	return { readOperand() };
}

type: copy_value
operands: { ValID, TypeID } 

{ OperandIDs, Attrs } readStore() {
	bool isInit = false;
	auto src = readOperand();
	expectAndConsume("to");
	consumeOptionalAttr("init", isInit);
	auto dest = readOperand();
	return { { src, dest }, isInit };
}

type: store
operands: { ValID, TypeID }, { ValID, TypeID }
attributes: "init"

Check:

void checkCopyValue(OperandIDs operands, Attrs attrs) {
	require(operands.size() == 0);
	// Other checks...
}

// Check store in the same way.

Parse:

SILInstruction *emitCopyValue(OperandIDs operands, Attrs attrs) {
	auto y = getLocalValue(operands.front.val, operands.front.type);
	return builder.createCopyValue(y);
}

Splitting the implementation up in this way gives us a few major wins. First and foremost, separation of concerns allows for all three steps to be as simple and straightforward as possible. Second, the verification step allows for much more sensical and intuitive sil parsing errors. Last, this approach models very closely after the serializer's implementation and will allow us to have interoperability between the two in the future.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment