diff --git a/passes/techmap/abc9.cc b/passes/techmap/abc9.cc
index 5ae2fb22a..0e2ca80c7 100644
--- a/passes/techmap/abc9.cc
+++ b/passes/techmap/abc9.cc
@@ -145,6 +145,11 @@ struct Abc9Pass : public ScriptPass
 		log("        generate netlist using luts. Use the specified costs for luts with 1,\n");
 		log("        2, 3, .. inputs.\n");
 		log("\n");
+		log("    -maxlut <width>\n");
+		log("        when auto-generating the lut library, discard all luts equal to or\n");
+		log("        greater than this size (applicable when neither -lut nor -luts is\n");
+		log("        specified).\n");
+		log("\n");
 		log("    -dff\n");
 		log("        also pass $_ABC9_FF_ cells through to ABC. modules with many clock\n");
 		log("        domains are marked as such and automatically partitioned by ABC.\n");
@@ -175,6 +180,8 @@ struct Abc9Pass : public ScriptPass
 
 	std::stringstream exe_cmd;
 	bool dff_mode, cleanup;
+	bool lut_mode;
+	int maxlut;
 	std::string box_file;
 
 	void clear_flags() YS_OVERRIDE
@@ -183,6 +190,8 @@ struct Abc9Pass : public ScriptPass
 		exe_cmd << "abc9_exe";
 		dff_mode = false;
 		cleanup = true;
+		lut_mode = false;
+		maxlut = 0;
 		box_file.clear();
 	}
 
@@ -204,9 +213,11 @@ struct Abc9Pass : public ScriptPass
 		for (argidx = 1; argidx < args.size(); argidx++) {
 			std::string arg = args[argidx];
 			if ((arg == "-exe" || arg == "-script" || arg == "-D" ||
-						/* arg == "-S" || */ arg == "-lut" || arg == "-luts" ||
+						/*arg == "-S" ||*/ arg == "-lut" || arg == "-luts" ||
 						/*arg == "-box" ||*/ arg == "-W") &&
 					argidx+1 < args.size()) {
+				if (arg == "-lut" || arg == "-luts")
+					lut_mode = true;
 				exe_cmd << " " << arg << " " << args[++argidx];
 				continue;
 			}
@@ -228,6 +239,10 @@ struct Abc9Pass : public ScriptPass
 				box_file = args[++argidx];
 				continue;
 			}
+			if (arg == "-maxlut" && argidx+1 < args.size()) {
+				maxlut = atoi(args[++argidx].c_str());
+				continue;
+			}
 			if (arg == "-run" && argidx+1 < args.size()) {
 				size_t pos = args[argidx+1].find(':');
 				if (pos == std::string::npos)
@@ -240,6 +255,9 @@ struct Abc9Pass : public ScriptPass
 		}
 		extra_args(args, argidx, design);
 
+		if (maxlut && lut_mode)
+			log_cmd_error("abc9 '-maxlut' option only applicable without '-lut' nor '-luts'.\n");
+
 		log_assert(design);
 		if (design->selected_modules().empty()) {
 			log_warning("No modules selected for ABC9 techmapping.\n");
@@ -263,6 +281,10 @@ struct Abc9Pass : public ScriptPass
 				run("abc9_ops -mark_scc -prep_delays -prep_xaiger [-dff]", "(option for -dff)");
 			else
 				run("abc9_ops -mark_scc -prep_delays -prep_xaiger" + std::string(dff_mode ? " -dff" : ""), "(option for -dff)");
+			if (help_mode)
+				run("abc9_ops -prep_lut <maxlut>", "(skip if -lut or -luts)");
+			else if (!lut_mode)
+				run(stringf("abc9_ops -prep_lut %d", maxlut));
 			run("select -set abc9_holes A:abc9_holes");
 			run("flatten -wb @abc9_holes");
 			run("techmap @abc9_holes");
@@ -276,9 +298,10 @@ struct Abc9Pass : public ScriptPass
 		if (check_label("map")) {
 			if (help_mode) {
 				run("foreach module in selection");
+				run("    abc9_ops -write_lut <abc-temp-dir>/input.lut", "(skip if '-lut' or '-luts')");
 				run("    abc9_ops -write_box [<value from -box>|(null)] <abc-temp-dir>/input.box");
 				run("    write_xaiger -map <abc-temp-dir>/input.sym <abc-temp-dir>/input.xaig");
-				run("    abc9_exe [options] -cwd <abc-temp-dir> -box <abc-temp-dir>/input.box");
+				run("    abc9_exe [options] -cwd <abc-temp-dir> [-lut <abc-temp-dir>/input.lut] -box <abc-temp-dir>/input.box");
 				run("    read_aiger -xaiger -wideports -module_name <module-name>$abc9 -map <abc-temp-dir>/input.sym <abc-temp-dir>/output.aig");
 				run("    abc9_ops -reintegrate");
 			}
@@ -304,6 +327,8 @@ struct Abc9Pass : public ScriptPass
 						tempdir_name[0] = tempdir_name[4] = '_';
 					tempdir_name = make_temp_dir(tempdir_name);
 
+					if (!lut_mode)
+						run(stringf("abc9_ops -write_lut %s/input.lut", tempdir_name.c_str()));
 					if (box_file.empty())
 						run(stringf("abc9_ops -write_box (null) %s/input.box", tempdir_name.c_str()));
 					else
@@ -319,7 +344,12 @@ struct Abc9Pass : public ScriptPass
 							active_design->scratchpad_get_int("write_xaiger.num_inputs"),
 							num_outputs);
 					if (num_outputs) {
-						run(stringf("%s -cwd %s -box %s/input.box", exe_cmd.str().c_str(), tempdir_name.c_str(), tempdir_name.c_str()));
+						std::string abc9_exe_cmd;
+						abc9_exe_cmd += stringf("%s -cwd %s", exe_cmd.str().c_str(), tempdir_name.c_str());
+						if (!lut_mode)
+							abc9_exe_cmd += stringf(" -lut %s/input.lut", tempdir_name.c_str());
+						abc9_exe_cmd += stringf(" -box %s/input.box", tempdir_name.c_str());
+						run(abc9_exe_cmd);
 						run(stringf("read_aiger -xaiger -wideports -module_name %s$abc9 -map %s/input.sym %s/output.aig", log_id(mod), tempdir_name.c_str(), tempdir_name.c_str()));
 						run("abc9_ops -reintegrate");
 					}
diff --git a/passes/techmap/abc9_ops.cc b/passes/techmap/abc9_ops.cc
index 54605f90e..cf6d5eabb 100644
--- a/passes/techmap/abc9_ops.cc
+++ b/passes/techmap/abc9_ops.cc
@@ -562,6 +562,56 @@ void prep_delays(RTLIL::Design *design)
 	design->scratchpad_set_string("abc9_ops.box.flops", ss.str());
 }
 
+void prep_lut(RTLIL::Design *design, int maxlut)
+{
+	std::stringstream ss;
+	std::vector<std::pair<int, std::string>> table;
+	for (auto module : design->modules()) {
+		auto it = module->attributes.find(ID(abc9_lut));
+		if (it == module->attributes.end())
+			continue;
+		SigBit o;
+		std::vector<int> specify;
+		for (auto cell : module->cells()) {
+			if (cell->type != ID($specify2))
+				continue;
+			log_assert(cell->getParam(ID(SRC_WIDTH)) == 1);
+			log_assert(cell->getParam(ID(DST_WIDTH)) == 1);
+			SigBit d = cell->getPort(ID(DST));
+			if (o == SigBit())
+				o = d;
+			else
+				log_assert(o == d);
+			int rise_max = cell->getParam(ID(T_RISE_MAX)).as_int();
+			int fall_max = cell->getParam(ID(T_FALL_MAX)).as_int();
+			specify.push_back(std::max(rise_max,fall_max));
+		}
+		if (maxlut && GetSize(specify) > maxlut)
+			continue;
+		std::sort(specify.begin(), specify.end());
+		ss.str("");
+		ss << "# " << module->name.str() << std::endl;
+		ss << GetSize(specify) << " " << it->second.as_int();
+		for (auto i : specify)
+			ss << " " << i;
+		ss << std::endl;
+		table.emplace_back(GetSize(specify), ss.str());
+	}
+	// ABC expects ascending size
+	std::sort(table.begin(), table.end());
+	ss.str("");
+	for (auto &i : table)
+		ss << i.second;
+	design->scratchpad_set_string("abc9_ops.lut_library", ss.str());
+}
+
+void write_lut(RTLIL::Module *module, const std::string &dst) {
+	std::ofstream ofs(dst);
+	log_assert(ofs.is_open());
+	ofs << module->design->scratchpad_get_string("abc9_ops.lut_library");
+	ofs.close();
+}
+
 void write_box(RTLIL::Module *module, const std::string &src, const std::string &dst) {
 	std::ofstream ofs(dst);
 	log_assert(ofs.is_open());
@@ -1002,6 +1052,12 @@ struct Abc9OpsPass : public Pass {
 		log("        compute the clock domain and initial value of each flop in the design.\n");
 		log("        process the '$holes' module to support clock-enable functionality.\n");
 		log("\n");
+		log("    -prep_lut <maxlut>\n");
+		log("        pre-compute the lut library.\n");
+		log("\n");
+		log("    -write_lut <dst>\n");
+		log("        TODO.\n");
+		log("\n");
 		log("    -write_box (<src>|(null)) <dst>\n");
 		log("        copy the existing box file from <src> (skip if '(null)') and append any\n");
 		log("        new box definitions.\n");
@@ -1021,8 +1077,11 @@ struct Abc9OpsPass : public Pass {
 		bool mark_scc_mode = false;
 		bool prep_dff_mode = false;
 		bool prep_xaiger_mode = false;
+		bool prep_lut_mode = false;
 		bool reintegrate_mode = false;
 		bool dff_mode = false;
+		std::string write_lut_dst;
+		int maxlut = 0;
 		std::string write_box_src, write_box_dst;
 
 		size_t argidx;
@@ -1048,6 +1107,19 @@ struct Abc9OpsPass : public Pass {
 				prep_delays_mode = true;
 				continue;
 			}
+			if (arg == "-prep_lut" && argidx+1 < args.size()) {
+				prep_lut_mode = true;
+				maxlut = atoi(args[++argidx].c_str());
+				continue;
+			}
+			if (arg == "-write_lut" && argidx+1 < args.size()) {
+				write_lut_dst = args[++argidx];
+				rewrite_filename(write_lut_dst);
+				continue;
+			}
+			if (arg == "-maxlut" && argidx+1 < args.size()) {
+				continue;
+			}
 			if (arg == "-write_box" && argidx+2 < args.size()) {
 				write_box_src = args[++argidx];
 				write_box_dst = args[++argidx];
@@ -1067,16 +1139,21 @@ struct Abc9OpsPass : public Pass {
 		}
 		extra_args(args, argidx, design);
 
-		if (!(check_mode || mark_scc_mode || prep_delays_mode || prep_xaiger_mode || prep_dff_mode || !write_box_src.empty() || reintegrate_mode))
-			log_cmd_error("At least one of -check, -mark_scc, -prep_{delays,xaiger,dff}, -write_box, -reintegrate must be specified.\n");
+		if (!(check_mode || mark_scc_mode || prep_delays_mode || prep_xaiger_mode || prep_dff_mode || prep_lut_mode || !write_lut_dst.empty() || !write_box_dst.empty() || reintegrate_mode))
+			log_cmd_error("At least one of -check, -mark_scc, -prep_{delays,xaiger,dff,lut}, -write_{lut,box}, -reintegrate must be specified.\n");
 
 		if (dff_mode && !prep_xaiger_mode)
 			log_cmd_error("'-dff' option is only relevant for -prep_xaiger.\n");
 
+		if (maxlut && !write_lut_dst.empty())
+			log_cmd_error("'-maxlut' option is only relevant for -prep_lut.\n");
+
 		if (check_mode)
 			check(design);
 		if (prep_delays_mode)
 			prep_delays(design);
+		if (prep_lut_mode)
+			prep_lut(design, maxlut);
 
 		for (auto mod : design->selected_modules()) {
 			if (mod->get_bool_attribute("\\abc9_holes"))
@@ -1090,7 +1167,9 @@ struct Abc9OpsPass : public Pass {
 			if (!design->selected_whole_module(mod))
 				log_error("Can't handle partially selected module %s!\n", log_id(mod));
 
-			if (!write_box_src.empty())
+			if (!write_lut_dst.empty())
+				write_lut(mod, write_lut_dst);
+			if (!write_box_dst.empty())
 				write_box(mod, write_box_src, write_box_dst);
 			if (mark_scc_mode)
 				mark_scc(mod);
diff --git a/techlibs/xilinx/cells_sim.v b/techlibs/xilinx/cells_sim.v
index 4692eba33..a1fe56fa1 100644
--- a/techlibs/xilinx/cells_sim.v
+++ b/techlibs/xilinx/cells_sim.v
@@ -162,32 +162,55 @@ module INV(
   assign O = !I;
 endmodule
 
+(* abc9_lut=1 *)
 module LUT1(output O, input I0);
   parameter [1:0] INIT = 0;
   assign O = I0 ? INIT[1] : INIT[0];
+  specify
+    (I0 => O) = 127;
+  endspecify
 endmodule
 
+(* abc9_lut=2 *)
 module LUT2(output O, input I0, I1);
   parameter [3:0] INIT = 0;
   wire [ 1: 0] s1 = I1 ? INIT[ 3: 2] : INIT[ 1: 0];
   assign O = I0 ? s1[1] : s1[0];
+  specify
+    (I0 => O) = 238;
+    (I1 => O) = 127;
+  endspecify
 endmodule
 
+(* abc9_lut=3 *)
 module LUT3(output O, input I0, I1, I2);
   parameter [7:0] INIT = 0;
   wire [ 3: 0] s2 = I2 ? INIT[ 7: 4] : INIT[ 3: 0];
   wire [ 1: 0] s1 = I1 ?   s2[ 3: 2] :   s2[ 1: 0];
   assign O = I0 ? s1[1] : s1[0];
+  specify
+    (I0 => O) = 407;
+    (I1 => O) = 238;
+    (I2 => O) = 127;
+  endspecify
 endmodule
 
+(* abc9_lut=3 *)
 module LUT4(output O, input I0, I1, I2, I3);
   parameter [15:0] INIT = 0;
   wire [ 7: 0] s3 = I3 ? INIT[15: 8] : INIT[ 7: 0];
   wire [ 3: 0] s2 = I2 ?   s3[ 7: 4] :   s3[ 3: 0];
   wire [ 1: 0] s1 = I1 ?   s2[ 3: 2] :   s2[ 1: 0];
   assign O = I0 ? s1[1] : s1[0];
+  specify
+    (I0 => O) = 472;
+    (I1 => O) = 407;
+    (I2 => O) = 238;
+    (I3 => O) = 127;
+  endspecify
 endmodule
 
+(* abc9_lut=3 *)
 module LUT5(output O, input I0, I1, I2, I3, I4);
   parameter [31:0] INIT = 0;
   wire [15: 0] s4 = I4 ? INIT[31:16] : INIT[15: 0];
@@ -195,8 +218,16 @@ module LUT5(output O, input I0, I1, I2, I3, I4);
   wire [ 3: 0] s2 = I2 ?   s3[ 7: 4] :   s3[ 3: 0];
   wire [ 1: 0] s1 = I1 ?   s2[ 3: 2] :   s2[ 1: 0];
   assign O = I0 ? s1[1] : s1[0];
+  specify
+    (I0 => O) = 631;
+    (I1 => O) = 472;
+    (I2 => O) = 407;
+    (I3 => O) = 238;
+    (I4 => O) = 127;
+  endspecify
 endmodule
 
+(* abc9_lut=5 *)
 module LUT6(output O, input I0, I1, I2, I3, I4, I5);
   parameter [63:0] INIT = 0;
   wire [31: 0] s5 = I5 ? INIT[63:32] : INIT[31: 0];
@@ -205,6 +236,14 @@ module LUT6(output O, input I0, I1, I2, I3, I4, I5);
   wire [ 3: 0] s2 = I2 ?   s3[ 7: 4] :   s3[ 3: 0];
   wire [ 1: 0] s1 = I1 ?   s2[ 3: 2] :   s2[ 1: 0];
   assign O = I0 ? s1[1] : s1[0];
+  specify
+    (I0 => O) = 642;
+    (I1 => O) = 631;
+    (I2 => O) = 472;
+    (I3 => O) = 407;
+    (I4 => O) = 238;
+    (I5 => O) = 127;
+  endspecify
 endmodule
 
 module LUT6_2(output O6, output O5, input I0, I1, I2, I3, I4, I5);
@@ -223,6 +262,50 @@ module LUT6_2(output O6, output O5, input I0, I1, I2, I3, I4, I5);
   assign O5 = I0 ? s5_1[1] : s5_1[0];
 endmodule
 
+(* abc9_lut=10 *)
+module $__ABC9_LUT7(output O, input I0, I1, I2, I3, I4, I5, I6);
+  parameter [127:0] INIT = 0;
+  wire [63: 0] s6 = I6 ? INIT[127:64] : INIT[63: 0];
+  wire [31: 0] s5 = I5 ?    s6[63:32] :   s6[31: 0];
+  wire [15: 0] s4 = I4 ?    s5[31:16] :   s5[15: 0];
+  wire [ 7: 0] s3 = I3 ?    s4[15: 8] :   s4[ 7: 0];
+  wire [ 3: 0] s2 = I2 ?    s3[ 7: 4] :   s3[ 3: 0];
+  wire [ 1: 0] s1 = I1 ?    s2[ 3: 2] :   s2[ 1: 0];
+  assign O = I0 ? s1[1] : s1[0];
+  specify
+    (I0 => O) = 1028;
+    (I1 => O) = 1017;
+    (I2 => O) =  858;
+    (I3 => O) =  793;
+    (I4 => O) =  624;
+    (I5 => O) =  513;
+    (I6 => O) =  464;
+  endspecify
+endmodule
+
+(* abc9_lut=20 *)
+module $__ABC9_LUT8(output O, input I0, I1, I2, I3, I4, I5, I6, I7);
+  parameter [255:0] INIT = 0;
+  wire [127: 0] s7 = I7 ? INIT[255:128] : INIT[127: 0];
+  wire [ 63: 0] s6 = I6 ?   s7[127:63]  :   s7[ 64: 0];
+  wire [ 31: 0] s5 = I5 ?   s6[ 63:32]  :   s6[ 31: 0];
+  wire [ 15: 0] s4 = I4 ?   s5[ 31:16]  :   s5[ 15: 0];
+  wire [  7: 0] s3 = I3 ?   s4[ 15: 8]  :   s4[  7: 0];
+  wire [  3: 0] s2 = I2 ?   s3[  7: 4]  :   s3[  3: 0];
+  wire [  1: 0] s1 = I1 ?   s2[  3: 2]  :   s2[  1: 0];
+  assign O = I0 ? s1[1] : s1[0];
+  specify
+    (I0 => O) = 1149;
+    (I1 => O) = 1138;
+    (I2 => O) =  979;
+    (I3 => O) =  914;
+    (I4 => O) =  745;
+    (I5 => O) =  634;
+    (I6 => O) =  585;
+    (I7 => O) =  468;
+  endspecify
+endmodule
+
 module MUXCY(output O, input CI, DI, S);
   assign O = S ? CI : DI;
 endmodule
diff --git a/techlibs/xilinx/synth_xilinx.cc b/techlibs/xilinx/synth_xilinx.cc
index a6b422b83..4614a2bf9 100644
--- a/techlibs/xilinx/synth_xilinx.cc
+++ b/techlibs/xilinx/synth_xilinx.cc
@@ -354,7 +354,7 @@ struct SynthXilinxPass : public ScriptPass
 			std::string read_args;
 			if (vpr)
 				read_args += " -D_EXPLICIT_CARRY";
-			read_args += " -lib +/xilinx/cells_sim.v";
+			read_args += " -lib -specify +/xilinx/cells_sim.v";
 			run("read_verilog" + read_args);
 
 			run("read_verilog -lib +/xilinx/cells_xtra.v");
@@ -627,9 +627,7 @@ struct SynthXilinxPass : public ScriptPass
 				else
 					abc9_opts += stringf(" -W %s", RTLIL::constpad.at(k, RTLIL::constpad.at("synth_xilinx.abc9.xc7.W")).c_str());
 				if (nowidelut)
-					abc9_opts += " -lut +/xilinx/abc9_xc7_nowide.lut";
-				else
-					abc9_opts += " -lut +/xilinx/abc9_xc7.lut";
+					abc9_opts += stringf(" -maxlut %d", lut_size_s);
 				if (dff_mode)
 					abc9_opts += " -dff";
 				run("abc9" + abc9_opts);