diff --git a/libsolidity/CMakeLists.txt b/libsolidity/CMakeLists.txt
index 88c1a31b1..572038d22 100644
--- a/libsolidity/CMakeLists.txt
+++ b/libsolidity/CMakeLists.txt
@@ -73,6 +73,8 @@ set(sources
 	codegen/ir/IRGeneratorForStatements.h
 	codegen/ir/IRGenerationContext.cpp
 	codegen/ir/IRGenerationContext.h
+	codegen/ir/IRLValue.cpp
+	codegen/ir/IRLValue.h
 	formal/EncodingContext.cpp
 	formal/EncodingContext.h
 	formal/SMTChecker.cpp
diff --git a/libsolidity/codegen/YulUtilFunctions.cpp b/libsolidity/codegen/YulUtilFunctions.cpp
index e8ca19f2a..42b81d34b 100644
--- a/libsolidity/codegen/YulUtilFunctions.cpp
+++ b/libsolidity/codegen/YulUtilFunctions.cpp
@@ -237,6 +237,30 @@ string YulUtilFunctions::shiftRightFunction(size_t _numBits)
 	}
 }
 
+string YulUtilFunctions::updateByteSliceFunction(size_t _numBytes, size_t _shiftBytes)
+{
+	solAssert(_numBytes <= 32, "");
+	solAssert(_shiftBytes <= 32, "");
+	size_t numBits = _numBytes * 8;
+	size_t shiftBits = _shiftBytes * 8;
+	string functionName = "update_byte_slice_" + to_string(_numBytes) + "_shift_" + to_string(_shiftBytes);
+	return m_functionCollector->createFunction(functionName, [&]() {
+		return
+			Whiskers(R"(
+			function <functionName>(value, toInsert) -> result {
+				let mask := <mask>
+				toInsert := <shl>(toInsert)
+				value := and(value, not(mask))
+				result := or(value, and(toInsert, mask))
+			}
+			)")
+			("functionName", functionName)
+			("mask", formatNumber(((bigint(1) << numBits) - 1) << shiftBits))
+			("shl", shiftLeftFunction(shiftBits))
+			.render();
+	});
+}
+
 string YulUtilFunctions::roundUpFunction()
 {
 	string functionName = "round_up_to_mul_of_32";
@@ -541,6 +565,26 @@ string YulUtilFunctions::cleanupFromStorageFunction(Type const& _type, bool _spl
 	});
 }
 
+string YulUtilFunctions::prepareStoreFunction(Type const& _type)
+{
+	solUnimplementedAssert(_type.category() != Type::Category::Function, "");
+
+	string functionName = "prepare_store_" + _type.identifier();
+	return m_functionCollector->createFunction(functionName, [&]() {
+		Whiskers templ(R"(
+			function <functionName>(value) -> ret {
+				ret := <actualPrepare>
+			}
+		)");
+		templ("functionName", functionName);
+		if (_type.category() == Type::Category::FixedBytes)
+			templ("actualPrepare", shiftRightFunction(256 - 8 * _type.storageBytes()) + "(value)");
+		else
+			templ("actualPrepare", "value");
+		return templ.render();
+	});
+}
+
 string YulUtilFunctions::allocationFunction()
 {
 	string functionName = "allocateMemory";
diff --git a/libsolidity/codegen/YulUtilFunctions.h b/libsolidity/codegen/YulUtilFunctions.h
index e22494b87..cca8ec209 100644
--- a/libsolidity/codegen/YulUtilFunctions.h
+++ b/libsolidity/codegen/YulUtilFunctions.h
@@ -69,6 +69,11 @@ public:
 	std::string shiftLeftFunction(size_t _numBits);
 	std::string shiftRightFunction(size_t _numBits);
 
+	/// @returns the name of a function f(value, toInsert) -> newValue which replaces the
+	/// _numBytes bytes starting at byte position _shiftBytes (counted from the least significant
+	/// byte) by the _numBytes least significant bytes of `toInsert`.
+	std::string updateByteSliceFunction(size_t _numBytes, size_t _shiftBytes);
+
 	/// @returns the name of a function that rounds its input to the next multiple
 	/// of 32 or the input if it is a multiple of 32.
 	std::string roundUpFunction();
@@ -110,6 +115,12 @@ public:
 	/// single variable.
 	std::string cleanupFromStorageFunction(Type const& _type, bool _splitFunctionTypes);
 
+	/// @returns the name of a function that prepares a value of the given type
+	/// for being stored in storage. This usually includes cleanup and right-alignment
+	/// to fit the number of bytes in storage.
+	/// The resulting value might still have dirty higher order bits.
+	std::string prepareStoreFunction(Type const& _type);
+
 	/// @returns the name of a function that allocates memory.
 	/// Modifies the "free memory pointer"
 	/// Arguments: size
diff --git a/libsolidity/codegen/ir/IRGenerationContext.cpp b/libsolidity/codegen/ir/IRGenerationContext.cpp
index f0f076db6..9b7d3f169 100644
--- a/libsolidity/codegen/ir/IRGenerationContext.cpp
+++ b/libsolidity/codegen/ir/IRGenerationContext.cpp
@@ -39,7 +39,7 @@ string IRGenerationContext::addLocalVariable(VariableDeclaration const& _varDecl
 	return m_localVariables[&_varDecl] = "vloc_" + _varDecl.name() + "_" + to_string(_varDecl.id());
 }
 
-string IRGenerationContext::variableName(VariableDeclaration const& _varDecl)
+string IRGenerationContext::localVariableName(VariableDeclaration const& _varDecl)
 {
 	solAssert(
 		m_localVariables.count(&_varDecl),
@@ -48,6 +48,15 @@ string IRGenerationContext::variableName(VariableDeclaration const& _varDecl)
 	return m_localVariables[&_varDecl];
 }
 
+void IRGenerationContext::addStateVariable(
+	VariableDeclaration const& _declaration,
+	u256 _storageOffset,
+	unsigned _byteOffset
+)
+{
+	m_stateVariables[&_declaration] = make_pair(move(_storageOffset), _byteOffset);
+}
+
 string IRGenerationContext::functionName(FunctionDefinition const& _function)
 {
 	// @TODO previously, we had to distinguish creation context and runtime context,
@@ -128,3 +137,8 @@ string IRGenerationContext::internalDispatch(size_t _in, size_t _out)
 		return templ.render();
 	});
 }
+
+YulUtilFunctions IRGenerationContext::utils()
+{
+	return YulUtilFunctions(m_evmVersion, m_functions);
+}
diff --git a/libsolidity/codegen/ir/IRGenerationContext.h b/libsolidity/codegen/ir/IRGenerationContext.h
index d40a0f260..ec5c9cd31 100644
--- a/libsolidity/codegen/ir/IRGenerationContext.h
+++ b/libsolidity/codegen/ir/IRGenerationContext.h
@@ -26,6 +26,8 @@
 
 #include <liblangutil/EVMVersion.h>
 
+#include <libdevcore/Common.h>
+
 #include <string>
 #include <memory>
 #include <vector>
@@ -39,6 +41,7 @@ class ContractDefinition;
 class VariableDeclaration;
 class FunctionDefinition;
 class Expression;
+class YulUtilFunctions;
 
 /**
  * Class that contains contextual information during IR generation.
@@ -62,7 +65,16 @@ public:
 
 
 	std::string addLocalVariable(VariableDeclaration const& _varDecl);
-	std::string variableName(VariableDeclaration const& _varDecl);
+	bool isLocalVariable(VariableDeclaration const& _varDecl) const { return m_localVariables.count(&_varDecl); }
+	std::string localVariableName(VariableDeclaration const& _varDecl);
+
+	void addStateVariable(VariableDeclaration const& _varDecl, u256 _storageOffset, unsigned _byteOffset);
+	bool isStateVariable(VariableDeclaration const& _varDecl) const { return m_stateVariables.count(&_varDecl); }
+	std::pair<u256, unsigned> storageLocationOfVariable(VariableDeclaration const& _varDecl) const
+	{
+		return m_stateVariables.at(&_varDecl);
+	}
+
 	std::string functionName(FunctionDefinition const& _function);
 	FunctionDefinition const& virtualFunction(FunctionDefinition const& _functionDeclaration);
 	std::string virtualFunctionName(FunctionDefinition const& _functionDeclaration);
@@ -74,11 +86,16 @@ public:
 
 	std::string internalDispatch(size_t _in, size_t _out);
 
+	/// @returns a new copy of the utility function generator (but using the same function set).
+	YulUtilFunctions utils();
+
 private:
 	langutil::EVMVersion m_evmVersion;
 	OptimiserSettings m_optimiserSettings;
 	std::vector<ContractDefinition const*> m_inheritanceHierarchy;
 	std::map<VariableDeclaration const*, std::string> m_localVariables;
+	/// Storage offsets of state variables
+	std::map<VariableDeclaration const*, std::pair<u256, unsigned>> m_stateVariables;
 	std::shared_ptr<MultiUseYulFunctionCollector> m_functions;
 	size_t m_varCounter = 0;
 };
diff --git a/libsolidity/codegen/ir/IRGenerator.cpp b/libsolidity/codegen/ir/IRGenerator.cpp
index a2bea3992..d4560ef25 100644
--- a/libsolidity/codegen/ir/IRGenerator.cpp
+++ b/libsolidity/codegen/ir/IRGenerator.cpp
@@ -89,15 +89,14 @@ string IRGenerator::generate(ContractDefinition const& _contract)
 		}
 	)");
 
-	resetContext();
-	m_context.setInheritanceHierarchy(_contract.annotation().linearizedBaseContracts);
+	resetContext(_contract);
 	t("CreationObject", creationObjectName(_contract));
 	t("memoryInit", memoryInit());
 	t("constructor", _contract.constructor() ? constructorCode(*_contract.constructor()) : "");
 	t("deploy", deployCode(_contract));
 	t("functions", m_context.functionCollector()->requestedFunctions());
 
-	resetContext();
+	resetContext(_contract);
 	m_context.setInheritanceHierarchy(_contract.annotation().linearizedBaseContracts);
 	t("RuntimeObject", runtimeObjectName(_contract));
 	t("dispatch", dispatchRoutine(_contract));
@@ -249,7 +248,7 @@ string IRGenerator::memoryInit()
 		.render();
 }
 
-void IRGenerator::resetContext()
+void IRGenerator::resetContext(ContractDefinition const& _contract)
 {
 	solAssert(
 		m_context.functionCollector()->requestedFunctions().empty(),
@@ -257,4 +256,8 @@ void IRGenerator::resetContext()
 	);
 	m_context = IRGenerationContext(m_evmVersion, m_optimiserSettings);
 	m_utils = YulUtilFunctions(m_evmVersion, m_context.functionCollector());
+
+	m_context.setInheritanceHierarchy(_contract.annotation().linearizedBaseContracts);
+	for (auto const& var: ContractType(_contract).stateVariables())
+		m_context.addStateVariable(*get<0>(var), get<1>(var), get<2>(var));
 }
diff --git a/libsolidity/codegen/ir/IRGenerator.h b/libsolidity/codegen/ir/IRGenerator.h
index d78acfabe..612682a3b 100644
--- a/libsolidity/codegen/ir/IRGenerator.h
+++ b/libsolidity/codegen/ir/IRGenerator.h
@@ -68,7 +68,7 @@ private:
 
 	std::string memoryInit();
 
-	void resetContext();
+	void resetContext(ContractDefinition const& _contract);
 
 	langutil::EVMVersion const m_evmVersion;
 	OptimiserSettings const m_optimiserSettings;
diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp
index 57fecc061..3c6d02c87 100644
--- a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp
+++ b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp
@@ -21,6 +21,7 @@
 #include <libsolidity/codegen/ir/IRGeneratorForStatements.h>
 
 #include <libsolidity/codegen/ir/IRGenerationContext.h>
+#include <libsolidity/codegen/ir/IRLValue.h>
 #include <libsolidity/codegen/YulUtilFunctions.h>
 #include <libsolidity/ast/TypeProvider.h>
 
@@ -66,7 +67,7 @@ struct CopyTranslate: public yul::ASTCopier
 
 		return yul::Identifier{
 			_identifier.location,
-			yul::YulString{m_context.variableName(*varDecl)}
+			yul::YulString{m_context.localVariableName(*varDecl)}
 		};
 	}
 
@@ -79,6 +80,12 @@ private:
 
 
 
+string IRGeneratorForStatements::code() const
+{
+	solAssert(!m_currentLValue, "LValue not reset!");
+	return m_code.str();
+}
+
 bool IRGeneratorForStatements::visit(VariableDeclarationStatement const& _varDeclStatement)
 {
 	for (auto const& decl: _varDeclStatement.declarations())
@@ -94,7 +101,7 @@ bool IRGeneratorForStatements::visit(VariableDeclarationStatement const& _varDec
 		VariableDeclaration const& varDecl = *_varDeclStatement.declarations().front();
 		m_code <<
 			"let " <<
-			m_context.variableName(varDecl) <<
+			m_context.localVariableName(varDecl) <<
 			" := " <<
 			expressionAsType(*expression, *varDecl.type()) <<
 			"\n";
@@ -102,7 +109,7 @@ bool IRGeneratorForStatements::visit(VariableDeclarationStatement const& _varDec
 	else
 		for (auto const& decl: _varDeclStatement.declarations())
 			if (decl)
-				m_code << "let " << m_context.variableName(*decl) << "\n";
+				m_code << "let " << m_context.localVariableName(*decl) << "\n";
 
 	return false;
 }
@@ -112,17 +119,18 @@ bool IRGeneratorForStatements::visit(Assignment const& _assignment)
 	solUnimplementedAssert(_assignment.assignmentOperator() == Token::Assign, "");
 
 	_assignment.rightHandSide().accept(*this);
+	Type const* intermediateType = _assignment.rightHandSide().annotation().type->closestTemporaryType(
+		_assignment.leftHandSide().annotation().type
+	);
+	string intermediateValue = m_context.newYulVariable();
+	m_code << "let " << intermediateValue << " := " << expressionAsType(_assignment.rightHandSide(), *intermediateType) << "\n";
 
-	// TODO proper lvalue handling
-	auto const& lvalue = dynamic_cast<Identifier const&>(_assignment.leftHandSide());
-	string varName = m_context.variableName(dynamic_cast<VariableDeclaration const&>(*lvalue.annotation().referencedDeclaration));
+	_assignment.leftHandSide().accept(*this);
+	solAssert(!!m_currentLValue, "LValue not retrieved.");
+	m_currentLValue->storeValue(intermediateValue, *intermediateType);
+	m_currentLValue.reset();
 
-	m_code <<
-		varName <<
-		" := " <<
-		expressionAsType(_assignment.rightHandSide(), *lvalue.annotation().type) <<
-		"\n";
-	defineExpression(_assignment) << varName << "\n";
+	defineExpression(_assignment) << intermediateValue << "\n";
 
 	return false;
 }
@@ -179,7 +187,7 @@ bool IRGeneratorForStatements::visit(Return const& _return)
 		// TODO support tuples
 		solUnimplementedAssert(types.size() == 1, "Multi-returns not implemented.");
 		m_code <<
-			m_context.variableName(*returnParameters.front()) <<
+			m_context.localVariableName(*returnParameters.front()) <<
 			" := " <<
 			expressionAsType(*value, *types.front()) <<
 			"\n";
@@ -329,14 +337,26 @@ bool IRGeneratorForStatements::visit(InlineAssembly const& _inlineAsm)
 bool IRGeneratorForStatements::visit(Identifier const& _identifier)
 {
 	Declaration const* declaration = _identifier.annotation().referencedDeclaration;
-	string value;
 	if (FunctionDefinition const* functionDef = dynamic_cast<FunctionDefinition const*>(declaration))
-		value = to_string(m_context.virtualFunction(*functionDef).id());
+		defineExpression(_identifier) << to_string(m_context.virtualFunction(*functionDef).id()) << "\n";
 	else if (VariableDeclaration const* varDecl = dynamic_cast<VariableDeclaration const*>(declaration))
-		value = m_context.variableName(*varDecl);
+	{
+		// TODO for the constant case, we have to be careful:
+		// If the value is visited twice, `defineExpression` is called twice on
+		// the same expression.
+		solUnimplementedAssert(!varDecl->isConstant(), "");
+		unique_ptr<IRLValue> lvalue;
+		if (m_context.isLocalVariable(*varDecl))
+			lvalue = make_unique<IRLocalVariable>(m_code, m_context, *varDecl);
+		else if (m_context.isStateVariable(*varDecl))
+			lvalue = make_unique<IRStorageItem>(m_code, m_context, *varDecl);
+		else
+			solAssert(false, "Invalid variable kind.");
+
+		setLValue(_identifier, move(lvalue));
+	}
 	else
 		solUnimplemented("");
-	defineExpression(_identifier) << value << "\n";
 	return false;
 }
 
@@ -375,3 +395,14 @@ ostream& IRGeneratorForStatements::defineExpression(Expression const& _expressio
 {
 	return m_code << "let " << m_context.variable(_expression) << " := ";
 }
+
+void IRGeneratorForStatements::setLValue(Expression const& _expression, unique_ptr<IRLValue> _lvalue)
+{
+	solAssert(!m_currentLValue, "");
+
+	if (_expression.annotation().lValueRequested)
+		// Do not define the expression, so it cannot be used as value.
+		m_currentLValue = std::move(_lvalue);
+	else
+		defineExpression(_expression) << _lvalue->retrieveValue() << "\n";
+}
diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.h b/libsolidity/codegen/ir/IRGeneratorForStatements.h
index 5f5259463..5f196ad7d 100644
--- a/libsolidity/codegen/ir/IRGeneratorForStatements.h
+++ b/libsolidity/codegen/ir/IRGeneratorForStatements.h
@@ -21,6 +21,7 @@
 #pragma once
 
 #include <libsolidity/ast/ASTVisitor.h>
+#include <libsolidity/codegen/ir/IRLValue.h>
 
 namespace dev
 {
@@ -42,7 +43,7 @@ public:
 		m_utils(_utils)
 	{}
 
-	std::string code() const { return m_code.str(); }
+	std::string code() const;
 
 	bool visit(VariableDeclarationStatement const& _variableDeclaration) override;
 	bool visit(Assignment const& _assignment) override;
@@ -62,9 +63,12 @@ private:
 	std::string expressionAsType(Expression const& _expression, Type const& _to);
 	std::ostream& defineExpression(Expression const& _expression);
 
+	void setLValue(Expression const& _expression, std::unique_ptr<IRLValue> _lvalue);
+
 	std::ostringstream m_code;
 	IRGenerationContext& m_context;
 	YulUtilFunctions& m_utils;
+	std::unique_ptr<IRLValue> m_currentLValue;
 };
 
 }
diff --git a/libsolidity/codegen/ir/IRLValue.cpp b/libsolidity/codegen/ir/IRLValue.cpp
new file mode 100644
index 000000000..3c97992ff
--- /dev/null
+++ b/libsolidity/codegen/ir/IRLValue.cpp
@@ -0,0 +1,103 @@
+/*
+	This file is part of solidity.
+
+	solidity is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 3 of the License, or
+	(at your option) any later version.
+
+	solidity is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with solidity.  If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * Generator for code that handles LValues.
+ */
+
+#include <libsolidity/codegen/ir/IRLValue.h>
+
+#include <libsolidity/codegen/ir/IRGenerationContext.h>
+#include <libsolidity/codegen/YulUtilFunctions.h>
+#include <libsolidity/ast/AST.h>
+
+#include <libdevcore/Whiskers.h>
+
+using namespace std;
+using namespace dev;
+using namespace dev::solidity;
+
+IRLocalVariable::IRLocalVariable(
+	ostream& _code,
+	IRGenerationContext& _context,
+	VariableDeclaration const& _varDecl
+):
+	IRLValue(_code, _context, _varDecl.annotation().type),
+	m_variableName(_context.localVariableName(_varDecl))
+{
+}
+
+void IRLocalVariable::storeValue(string const& _value, Type const& _type) const
+{
+	solAssert(_type == *m_type, "Storing different types - not necessarily a problem.");
+	m_code << m_variableName << " := " << _value << "\n";
+}
+
+IRStorageItem::IRStorageItem(
+	ostream& _code,
+	IRGenerationContext& _context,
+	VariableDeclaration const& _varDecl
+):
+	IRLValue(_code, _context, _varDecl.annotation().type)
+{
+	u256 slot;
+	unsigned offset;
+	std::tie(slot, offset) = _context.storageLocationOfVariable(_varDecl);
+	m_slot = toCompactHexWithPrefix(slot);
+	m_offset = offset;
+}
+
+string IRStorageItem::retrieveValue() const
+{
+	if (!m_type->isValueType())
+		return m_slot;
+	solUnimplementedAssert(m_type->category() != Type::Category::Function, "");
+	return m_context.utils().readFromStorage(*m_type, m_offset, false) + "(" + m_slot + ")";
+}
+
+void IRStorageItem::storeValue(string const& _value, Type const& _sourceType) const
+{
+	if (m_type->isValueType())
+	{
+		solAssert(m_type->storageBytes() <= 32, "Invalid storage bytes size.");
+		solAssert(m_type->storageBytes() > 0, "Invalid storage bytes size.");
+		solAssert(m_type->storageBytes() + m_offset <= 32, "");
+
+		solAssert(_sourceType == *m_type, "Different type, but might not be an error.");
+
+		m_code <<
+			Whiskers("sstore(<slot>, <update>(sload(<slot>), <prepare>(<value>)))\n")
+			("slot", m_slot)
+			("update", m_context.utils().updateByteSliceFunction(m_type->storageBytes(), m_offset))
+			("prepare", m_context.utils().prepareStoreFunction(*m_type))
+			("value", _value)
+			.render();
+	}
+	else
+	{
+		solAssert(
+			_sourceType.category() == m_type->category(),
+			"Wrong type conversation for assignment."
+		);
+		if (m_type->category() == Type::Category::Array)
+			solUnimplementedAssert(false, "");
+		else if (m_type->category() == Type::Category::Struct)
+			solUnimplementedAssert(false, "");
+		else
+			solAssert(false, "Invalid non-value type for assignment.");
+	}
+}
+
diff --git a/libsolidity/codegen/ir/IRLValue.h b/libsolidity/codegen/ir/IRLValue.h
new file mode 100644
index 000000000..f5a980935
--- /dev/null
+++ b/libsolidity/codegen/ir/IRLValue.h
@@ -0,0 +1,95 @@
+/*
+	This file is part of solidity.
+
+	solidity is free software: you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation, either version 3 of the License, or
+	(at your option) any later version.
+
+	solidity is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with solidity.  If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * Generator for code that handles LValues.
+ */
+
+#pragma once
+
+#include <string>
+#include <ostream>
+
+namespace dev
+{
+namespace solidity
+{
+
+class VariableDeclaration;
+class IRGenerationContext;
+class Type;
+
+/**
+ * Abstract class used to retrieve, delete and store data in LValues.
+ */
+class IRLValue
+{
+protected:
+	IRLValue(std::ostream& _code, IRGenerationContext& _context, Type const* _type = nullptr):
+		m_code(_code),
+		m_context(_context),
+		m_type(_type)
+	{}
+
+public:
+	virtual ~IRLValue() = default;
+	/// @returns an expression to retrieve the value of the lvalue. This might also emit
+	/// code to m_code.
+	virtual std::string retrieveValue() const = 0;
+	/// Appends code to m_code to store the value of @a _value (should be an identifier)
+	/// of type @a _type in the lvalue. Might perform type conversion.
+	virtual void storeValue(std::string const& _value, Type const& _type) const = 0;
+
+protected:
+	std::ostream& m_code;
+	IRGenerationContext& m_context;
+	Type const* m_type;
+};
+
+class IRLocalVariable: public IRLValue
+{
+public:
+	IRLocalVariable(
+		std::ostream& _code,
+		IRGenerationContext& _context,
+		VariableDeclaration const& _varDecl
+	);
+	std::string retrieveValue() const override { return m_variableName; }
+	void storeValue(std::string const& _value, Type const& _type) const override;
+
+private:
+	std::string m_variableName;
+};
+
+class IRStorageItem: public IRLValue
+{
+public:
+	IRStorageItem(
+		std::ostream& _code,
+		IRGenerationContext& _context,
+		VariableDeclaration const& _varDecl
+	);
+	std::string retrieveValue() const override;
+	void storeValue(std::string const& _value, Type const& _type) const override;
+
+private:
+	std::string m_slot;
+	unsigned m_offset;
+};
+
+
+}
+}
diff --git a/test/libsolidity/SolidityExecutionFramework.h b/test/libsolidity/SolidityExecutionFramework.h
index a04b5373d..49c016699 100644
--- a/test/libsolidity/SolidityExecutionFramework.h
+++ b/test/libsolidity/SolidityExecutionFramework.h
@@ -92,7 +92,9 @@ public:
 			yul::AssemblyStack asmStack(
 				m_evmVersion,
 				yul::AssemblyStack::Language::StrictAssembly,
-				m_optimiserSettings
+				// Ignore optimiser settings here because we need Yul optimisation to
+				// get code that does not exhaust the stack.
+				OptimiserSettings::full()
 			);
 			if (!asmStack.parseAndAnalyze("", m_compiler.yulIROptimized(
 				_contractName.empty() ? m_compiler.lastContractName() : _contractName
diff --git a/test/libsolidity/semanticTests/viaYul/storage/packed_storage.sol b/test/libsolidity/semanticTests/viaYul/storage/packed_storage.sol
new file mode 100644
index 000000000..5ee7b6f4d
--- /dev/null
+++ b/test/libsolidity/semanticTests/viaYul/storage/packed_storage.sol
@@ -0,0 +1,16 @@
+contract C {
+    uint16 x;
+    byte y;
+    uint16 z;
+    function f(uint8 a) public returns (uint _x) {
+        x = a;
+        y = byte(uint8(x) + 1);
+        z = uint8(y) + 1;
+        x = z + 1;
+        _x = x;
+    }
+}
+// ====
+// compileViaYul: true
+// ----
+// f(uint8): 6 -> 9
diff --git a/test/libsolidity/semanticTests/viaYul/storage/simple_storage.sol b/test/libsolidity/semanticTests/viaYul/storage/simple_storage.sol
new file mode 100644
index 000000000..51bf2efcb
--- /dev/null
+++ b/test/libsolidity/semanticTests/viaYul/storage/simple_storage.sol
@@ -0,0 +1,17 @@
+contract C {
+    uint x;
+    uint y;
+    function setX(uint a) public returns (uint _x) {
+        x = a;
+        _x = x;
+    }
+    function setY(uint a) public returns (uint _y) {
+        y = a;
+        _y = y;
+    }
+}
+// ====
+// compileViaYul: true
+// ----
+// setX(uint256): 6 -> 6
+// setY(uint256): 2 -> 2