Multi-level intermediate representations (IR) show great promise for lowering the design costs for domain-specific compilers by providing a reusable, extensible, and non-opinionated framework for expressing domain-specific and high-level abstractions directly in the IR. But, while such frameworks support the progressive lowering of high-level representations to low-level IR, they do not raise in the opposite direction. Thus, the entry point into the compilation pipeline defines the highest level of abstraction for all subsequent transformations, limiting the set of applicable optimizations, in particular for general-purpose languages that are not semantically rich enough to model the required abstractions. We propose Progressive Raising, a complementary approach to the progressive lowering in multi-level IRs that raises from lower to higher-level abstractions to leverage domain-specific transformations for low-level representations. We further introduce Multi-Level Tactics, our declarative approach for progressive raising, implemented on top of the MLIR framework, and demonstrate the progressive raising from affine loop nests specified in a general-purpose language to high-level linear algebra operations. Our raising paths leverage subsequent high-level domain-specific transformations with significant performance improvements. Index Terms-MLIR, progressive raising, multi-level intermediate representation / * instantiate the context * / auto _i = m_Placeholder(), _j = m_Placeholder(); auto _A = m_ArrayPlaceholder(); auto matcher = m_Op(_A({2 * _i+1, _j+5})); Listing 6: Declarative access pattern matcher. For(For(For(For(access_callback())))); auto access_callback = [&a](Body loop) { { AccessPatternContext pctx(/ * MLIR ctx * /); auto _a = m_Placeholder(); auto _b = m_Placeholder(); auto _c = m_Placeholder(); auto _d = m_Placeholder(); auto _C = m_ArrayPlaceholder(); auto _A = m_ArrayPlaceholder(); auto _B = m_ArrayPlaceholder(); auto var0 = m_Op(_C({_a, _b, _c})); / * check the store is the last instruction in the block * / auto var1 = m_Op(_C({_a, _b, _c})); auto var2 = m_Op(_A({_a, _c, _d}));