Index: src/InitTweak/GenInit.cc
===================================================================
--- src/InitTweak/GenInit.cc	(revision 0536c03a6e246eaed6f60082bd9f1b239186a0a4)
+++ src/InitTweak/GenInit.cc	(revision 1e6f632fe210f10e8a5aeaa25a8be00593f7e333)
@@ -122,6 +122,32 @@
 	};
 
+	struct HoistArrayDimension_NoResolve final : public WithDeclsToAdd, public WithShortCircuiting, public WithGuards {
+		/// hoist dimension from array types in object declaration so that it uses a single
+		/// const variable of type size_t, so that side effecting array dimensions are only
+		/// computed once.
+		static void hoistArrayDimension( std::list< Declaration * > & translationUnit );
+
+		void premutate( ObjectDecl * objectDecl );
+		DeclarationWithType * postmutate( ObjectDecl * objectDecl );
+		void premutate( FunctionDecl *functionDecl );
+		// should not traverse into any of these declarations to find objects
+		// that need to be constructed or destructed
+		void premutate( AggregateDecl * ) { visit_children = false; }
+		void premutate( NamedTypeDecl * ) { visit_children = false; }
+		void premutate( FunctionType * ) { visit_children = false; }
+
+		void hoist( Type * type );
+
+		Type::StorageClasses storageClasses;
+		bool inFunction = false;
+	};
+
 	void genInit( std::list< Declaration * > & translationUnit ) {
-		HoistArrayDimension::hoistArrayDimension( translationUnit );
+		if (!useNewAST) {
+			HoistArrayDimension::hoistArrayDimension( translationUnit );
+		}
+		else {
+			HoistArrayDimension_NoResolve::hoistArrayDimension( translationUnit );
+		}
 		fixReturnStatements( translationUnit );
 
@@ -196,9 +222,7 @@
 
 			// need to resolve array dimensions in order to accurately determine if constexpr
-			if (!useNewAST) {
-				ResolvExpr::findSingleExpression( arrayType->dimension, Validate::SizeType->clone(), indexer );
-				// array is variable-length when the dimension is not constexpr
-				arrayType->isVarLen = ! isConstExpr( arrayType->dimension );
-			}
+			ResolvExpr::findSingleExpression( arrayType->dimension, Validate::SizeType->clone(), indexer );
+			// array is variable-length when the dimension is not constexpr
+			arrayType->isVarLen = ! isConstExpr( arrayType->dimension );
 			// don't need to hoist dimension if it's definitely pure - only need to if there's potential for side effects.
 			// xxx - hoisting has no side effects anyways, so don't skip since we delay resolve
@@ -218,4 +242,52 @@
 
 	void HoistArrayDimension::premutate( FunctionDecl * ) {
+		GuardValue( inFunction );
+		inFunction = true;
+	}
+
+	// precompute array dimension expression, because constructor generation may duplicate it,
+	// which would be incorrect if it is a side-effecting computation.
+	void HoistArrayDimension_NoResolve::hoistArrayDimension( std::list< Declaration * > & translationUnit ) {
+		PassVisitor<HoistArrayDimension_NoResolve> hoister;
+		mutateAll( translationUnit, hoister );
+	}
+
+	void HoistArrayDimension_NoResolve::premutate( ObjectDecl * objectDecl ) {
+		GuardValue( storageClasses );
+		storageClasses = objectDecl->get_storageClasses();
+	}
+
+	DeclarationWithType * HoistArrayDimension_NoResolve::postmutate( ObjectDecl * objectDecl ) {
+		hoist( objectDecl->get_type() );
+		return objectDecl;
+	}
+
+	void HoistArrayDimension_NoResolve::hoist( Type * type ) {
+		// if in function, generate const size_t var
+		static UniqueName dimensionName( "_array_dim" );
+
+		// C doesn't allow variable sized arrays at global scope or for static variables, so don't hoist dimension.
+		if ( ! inFunction ) return;
+		if ( storageClasses.is_static ) return;
+
+		if ( ArrayType * arrayType = dynamic_cast< ArrayType * >( type ) ) {
+			if ( ! arrayType->get_dimension() ) return; // xxx - recursive call to hoist?
+			// don't need to hoist dimension if it's definitely pure - only need to if there's potential for side effects.
+			// xxx - hoisting has no side effects anyways, so don't skip since we delay resolve
+			// still try to detect constant expressions
+			if ( ! Tuples::maybeImpure( arrayType->dimension ) ) return;
+
+			ObjectDecl * arrayDimension = new ObjectDecl( dimensionName.newName(), storageClasses, LinkageSpec::C, 0, Validate::SizeType->clone(), new SingleInit( arrayType->get_dimension() ) );
+			arrayDimension->get_type()->set_const( true );
+
+			arrayType->set_dimension( new VariableExpr( arrayDimension ) );
+			declsToAddBefore.push_back( arrayDimension );
+
+			hoist( arrayType->get_base() );
+			return;
+		}
+	}
+
+	void HoistArrayDimension_NoResolve::premutate( FunctionDecl * ) {
 		GuardValue( inFunction );
 		inFunction = true;
Index: src/SymTab/Validate.cc
===================================================================
--- src/SymTab/Validate.cc	(revision 0536c03a6e246eaed6f60082bd9f1b239186a0a4)
+++ src/SymTab/Validate.cc	(revision 1e6f632fe210f10e8a5aeaa25a8be00593f7e333)
@@ -271,5 +271,5 @@
 	};
 
-	struct ArrayLength : public WithIndexer {
+	struct InitializerLength {
 		/// for array types without an explicit length, compute the length and store it so that it
 		/// is known to the rest of the phases. For example,
@@ -282,4 +282,9 @@
 
 		void previsit( ObjectDecl * objDecl );
+	};
+
+	struct ArrayLength : public WithIndexer {
+		static void computeLength( std::list< Declaration * > & translationUnit );
+
 		void previsit( ArrayType * arrayType );
 	};
@@ -382,6 +387,10 @@
 					FixObjectType::fix, translationUnit);
 			}
-			Stats::Time::TimeCall("Array Length",
-				ArrayLength::computeLength, translationUnit);
+			Stats::Time::TimeCall("Initializer Length",
+				InitializerLength::computeLength, translationUnit);
+			if (!useNewAST) {
+				Stats::Time::TimeCall("Array Length",
+					ArrayLength::computeLength, translationUnit);
+			}
 			Stats::Time::TimeCall("Find Special Declarations",
 				Validate::findSpecialDecls, translationUnit);
@@ -1332,4 +1341,9 @@
 	}
 
+	void InitializerLength::computeLength( std::list< Declaration * > & translationUnit ) {
+		PassVisitor<InitializerLength> len;
+		acceptAll( translationUnit, len );
+	}
+
 	void ArrayLength::computeLength( std::list< Declaration * > & translationUnit ) {
 		PassVisitor<ArrayLength> len;
@@ -1337,5 +1351,5 @@
 	}
 
-	void ArrayLength::previsit( ObjectDecl * objDecl ) {
+	void InitializerLength::previsit( ObjectDecl * objDecl ) {
 		if ( ArrayType * at = dynamic_cast< ArrayType * >( objDecl->type ) ) {
 			if ( at->dimension ) return;
@@ -1347,14 +1361,12 @@
 
 	void ArrayLength::previsit( ArrayType * type ) {
-		if (!useNewAST) {
-			if ( type->dimension ) {
-				// need to resolve array dimensions early so that constructor code can correctly determine
-				// if a type is a VLA (and hence whether its elements need to be constructed)
-				ResolvExpr::findSingleExpression( type->dimension, Validate::SizeType->clone(), indexer );
-
-				// must re-evaluate whether a type is a VLA, now that more information is available
-				// (e.g. the dimension may have been an enumerator, which was unknown prior to this step)
-				type->isVarLen = ! InitTweak::isConstExpr( type->dimension );
-			}
+		if ( type->dimension ) {
+			// need to resolve array dimensions early so that constructor code can correctly determine
+			// if a type is a VLA (and hence whether its elements need to be constructed)
+			ResolvExpr::findSingleExpression( type->dimension, Validate::SizeType->clone(), indexer );
+
+			// must re-evaluate whether a type is a VLA, now that more information is available
+			// (e.g. the dimension may have been an enumerator, which was unknown prior to this step)
+			type->isVarLen = ! InitTweak::isConstExpr( type->dimension );
 		}
 	}
